From 2d7ced1d49a3e8cd2106421450674d016df93845 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Fri, 15 Mar 2024 22:25:46 -0400 Subject: [PATCH 001/114] Fix linter errors golangci-lint upgrade to v1.56.2 added more checks Relates to pion/.goassets#201 --- agent.go | 28 ++++++------ ..._on_selected_candidate_pair_change_test.go | 4 +- agent_stats.go | 6 +-- agent_test.go | 14 +++--- candidate_base.go | 2 +- candidate_relay_test.go | 6 +-- candidate_test.go | 4 +- connectivity_vnet_test.go | 2 +- gather.go | 2 +- gather_test.go | 44 +++++++++---------- rand_test.go | 2 +- transport.go | 2 +- transport_test.go | 2 +- 13 files changed, 60 insertions(+), 58 deletions(-) diff --git a/agent.go b/agent.go index 571cd5b..6d671b2 100644 --- a/agent.go +++ b/agent.go @@ -396,7 +396,7 @@ func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remoteP a.log.Debugf("Started agent: isControlling? %t, remoteUfrag: %q, remotePwd: %q", isControlling, remoteUfrag, remotePwd) - return a.run(a.context(), func(ctx context.Context, agent *Agent) { + return a.run(a.context(), func(_ context.Context, agent *Agent) { agent.isControlling = isControlling agent.remoteUfrag = remoteUfrag agent.remotePwd = remotePwd @@ -426,7 +426,7 @@ func (a *Agent) connectivityChecks() { checkingDuration := time.Time{} contact := func() { - if err := a.run(a.context(), func(ctx context.Context, a *Agent) { + if err := a.run(a.context(), func(_ context.Context, a *Agent) { defer func() { lastConnectionState = a.connectionState }() @@ -506,7 +506,7 @@ func (a *Agent) updateConnectionState(newState ConnectionState) { // Call handler after finishing current task since we may be holding the agent lock // and the handler may also require it - a.afterRun(func(ctx context.Context) { + a.afterRun(func(_ context.Context) { a.chanState <- newState }) } @@ -685,7 +685,7 @@ func (a *Agent) AddRemoteCandidate(c Candidate) error { } go func() { - if err := a.run(a.context(), func(ctx context.Context, agent *Agent) { + if err := a.run(a.context(), func(_ context.Context, agent *Agent) { // nolint: contextcheck agent.addRemoteCandidate(c) }); err != nil { @@ -717,7 +717,7 @@ func (a *Agent) resolveAndAddMulticastCandidate(c *CandidateHost) { return } - if err = a.run(a.context(), func(ctx context.Context, agent *Agent) { + if err = a.run(a.context(), func(_ context.Context, agent *Agent) { // nolint: contextcheck agent.addRemoteCandidate(c) }); err != nil { @@ -810,7 +810,7 @@ func (a *Agent) addRemoteCandidate(c Candidate) { } func (a *Agent) addCandidate(ctx context.Context, c Candidate, candidateConn net.PacketConn) error { - return a.run(ctx, func(ctx context.Context, agent *Agent) { + return a.run(ctx, func(context.Context, *Agent) { set := a.localCandidates[c.NetworkType()] for _, candidate := range set { if candidate.Equal(c) { @@ -846,7 +846,7 @@ func (a *Agent) addCandidate(ctx context.Context, c Candidate, candidateConn net func (a *Agent) GetRemoteCandidates() ([]Candidate, error) { var res []Candidate - err := a.run(a.context(), func(ctx context.Context, agent *Agent) { + err := a.run(a.context(), func(_ context.Context, agent *Agent) { var candidates []Candidate for _, set := range agent.remoteCandidates { candidates = append(candidates, set...) @@ -864,7 +864,7 @@ func (a *Agent) GetRemoteCandidates() ([]Candidate, error) { func (a *Agent) GetLocalCandidates() ([]Candidate, error) { var res []Candidate - err := a.run(a.context(), func(ctx context.Context, agent *Agent) { + err := a.run(a.context(), func(_ context.Context, agent *Agent) { var candidates []Candidate for _, set := range agent.localCandidates { candidates = append(candidates, set...) @@ -881,7 +881,7 @@ func (a *Agent) GetLocalCandidates() ([]Candidate, error) { // GetLocalUserCredentials returns the local user credentials func (a *Agent) GetLocalUserCredentials() (frag string, pwd string, err error) { valSet := make(chan struct{}) - err = a.run(a.context(), func(ctx context.Context, agent *Agent) { + err = a.run(a.context(), func(_ context.Context, agent *Agent) { frag = agent.localUfrag pwd = agent.localPwd close(valSet) @@ -896,7 +896,7 @@ func (a *Agent) GetLocalUserCredentials() (frag string, pwd string, err error) { // GetRemoteUserCredentials returns the remote user credentials func (a *Agent) GetRemoteUserCredentials() (frag string, pwd string, err error) { valSet := make(chan struct{}) - err = a.run(a.context(), func(ctx context.Context, agent *Agent) { + err = a.run(a.context(), func(_ context.Context, agent *Agent) { frag = agent.remoteUfrag pwd = agent.remotePwd close(valSet) @@ -1145,7 +1145,7 @@ func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr) // and returns true if it is an actual remote candidate func (a *Agent) validateNonSTUNTraffic(local Candidate, remote net.Addr) (Candidate, bool) { var remoteCandidate Candidate - if err := a.run(local.context(), func(ctx context.Context, agent *Agent) { + if err := a.run(local.context(), func(context.Context, *Agent) { remoteCandidate = a.findRemoteCandidate(local.NetworkType(), remote) if remoteCandidate != nil { remoteCandidate.seen(false) @@ -1202,7 +1202,7 @@ func (a *Agent) SetRemoteCredentials(remoteUfrag, remotePwd string) error { return ErrRemotePwdEmpty } - return a.run(a.context(), func(ctx context.Context, agent *Agent) { + return a.run(a.context(), func(_ context.Context, agent *Agent) { agent.remoteUfrag = remoteUfrag agent.remotePwd = remotePwd }) @@ -1239,7 +1239,7 @@ func (a *Agent) Restart(ufrag, pwd string) error { } var err error - if runErr := a.run(a.context(), func(ctx context.Context, agent *Agent) { + if runErr := a.run(a.context(), func(_ context.Context, agent *Agent) { if agent.gatheringState == GatheringStateGathering { agent.gatherCandidateCancel() } @@ -1272,7 +1272,7 @@ func (a *Agent) Restart(ufrag, pwd string) error { func (a *Agent) setGatheringState(newState GatheringState) error { done := make(chan struct{}) - if err := a.run(a.context(), func(ctx context.Context, agent *Agent) { + if err := a.run(a.context(), func(context.Context, *Agent) { if a.gatheringState != newState && newState == GatheringStateComplete { a.chanCandidate <- nil } diff --git a/agent_on_selected_candidate_pair_change_test.go b/agent_on_selected_candidate_pair_change_test.go index 78cb4d5..63b35ed 100644 --- a/agent_on_selected_candidate_pair_change_test.go +++ b/agent_on_selected_candidate_pair_change_test.go @@ -17,12 +17,12 @@ func TestOnSelectedCandidatePairChange(t *testing.T) { agent, candidatePair := fixtureTestOnSelectedCandidatePairChange(t) callbackCalled := make(chan struct{}, 1) - err := agent.OnSelectedCandidatePairChange(func(local, remote Candidate) { + err := agent.OnSelectedCandidatePairChange(func(_, _ Candidate) { close(callbackCalled) }) require.NoError(t, err) - err = agent.run(context.Background(), func(ctx context.Context, agent *Agent) { + err = agent.run(context.Background(), func(_ context.Context, agent *Agent) { agent.setSelectedPair(candidatePair) }) require.NoError(t, err) diff --git a/agent_stats.go b/agent_stats.go index b9ad718..9582cac 100644 --- a/agent_stats.go +++ b/agent_stats.go @@ -11,7 +11,7 @@ import ( // GetCandidatePairsStats returns a list of candidate pair stats func (a *Agent) GetCandidatePairsStats() []CandidatePairStats { var res []CandidatePairStats - err := a.run(a.context(), func(ctx context.Context, agent *Agent) { + err := a.run(a.context(), func(_ context.Context, agent *Agent) { result := make([]CandidatePairStats, 0, len(agent.checklist)) for _, cp := range agent.checklist { stat := CandidatePairStats{ @@ -57,7 +57,7 @@ func (a *Agent) GetCandidatePairsStats() []CandidatePairStats { // GetLocalCandidatesStats returns a list of local candidates stats func (a *Agent) GetLocalCandidatesStats() []CandidateStats { var res []CandidateStats - err := a.run(a.context(), func(ctx context.Context, agent *Agent) { + err := a.run(a.context(), func(_ context.Context, agent *Agent) { result := make([]CandidateStats, 0, len(agent.localCandidates)) for networkType, localCandidates := range agent.localCandidates { for _, c := range localCandidates { @@ -94,7 +94,7 @@ func (a *Agent) GetLocalCandidatesStats() []CandidateStats { // GetRemoteCandidatesStats returns a list of remote candidates stats func (a *Agent) GetRemoteCandidatesStats() []CandidateStats { var res []CandidateStats - err := a.run(a.context(), func(ctx context.Context, agent *Agent) { + err := a.run(a.context(), func(_ context.Context, agent *Agent) { result := make([]CandidateStats, 0, len(agent.remoteCandidates)) for networkType, remoteCandidates := range agent.remoteCandidates { for _, c := range remoteCandidates { diff --git a/agent_test.go b/agent_test.go index 291df4d..8913b2b 100644 --- a/agent_test.go +++ b/agent_test.go @@ -24,6 +24,8 @@ import ( "github.com/stretchr/testify/require" ) +const localhostIPStr = "127.0.0.1" + type BadAddr struct{} func (ba *BadAddr) Network() string { @@ -57,7 +59,7 @@ func TestHandlePeerReflexive(t *testing.T) { t.Run("UDP prflx candidate from handleInbound()", func(t *testing.T) { var config AgentConfig - runAgentTest(t, &config, func(ctx context.Context, a *Agent) { + runAgentTest(t, &config, func(_ context.Context, a *Agent) { a.selector = &controllingSelector{agent: a, log: a.log} hostConfig := CandidateHostConfig{ @@ -118,7 +120,7 @@ func TestHandlePeerReflexive(t *testing.T) { t.Run("Bad network type with handleInbound()", func(t *testing.T) { var config AgentConfig - runAgentTest(t, &config, func(ctx context.Context, a *Agent) { + runAgentTest(t, &config, func(_ context.Context, a *Agent) { a.selector = &controllingSelector{agent: a, log: a.log} hostConfig := CandidateHostConfig{ @@ -145,7 +147,7 @@ func TestHandlePeerReflexive(t *testing.T) { t.Run("Success from unknown remote, prflx candidate MUST only be created via Binding Request", func(t *testing.T) { var config AgentConfig - runAgentTest(t, &config, func(ctx context.Context, a *Agent) { + runAgentTest(t, &config, func(_ context.Context, a *Agent) { a.selector = &controllingSelector{agent: a, log: a.log} tID := [stun.TransactionIDSize]byte{} copy(tID[:], "ABC") @@ -440,7 +442,7 @@ func TestInboundValidity(t *testing.T) { t.Fatalf("Error constructing ice.Agent") } - err = a.run(context.Background(), func(ctx context.Context, a *Agent) { + err = a.run(context.Background(), func(_ context.Context, a *Agent) { a.selector = &controllingSelector{agent: a, log: a.log} // nolint: contextcheck a.handleInbound(buildMsg(stun.ClassRequest, a.localUfrag+":"+a.remoteUfrag, a.localPwd), local, remote) @@ -455,7 +457,7 @@ func TestInboundValidity(t *testing.T) { t.Run("Valid bind without fingerprint", func(t *testing.T) { var config AgentConfig - runAgentTest(t, &config, func(ctx context.Context, a *Agent) { + runAgentTest(t, &config, func(_ context.Context, a *Agent) { a.selector = &controllingSelector{agent: a, log: a.log} msg, err := stun.Build(stun.BindingRequest, stun.TransactionID, stun.NewUsername(a.localUfrag+":"+a.remoteUfrag), @@ -1120,7 +1122,7 @@ func TestConnectionStateFailedDeleteAllCandidates(t *testing.T) { <-isFailed done := make(chan struct{}) - assert.NoError(t, aAgent.run(context.Background(), func(ctx context.Context, agent *Agent) { + assert.NoError(t, aAgent.run(context.Background(), func(context.Context, *Agent) { assert.Equal(t, len(aAgent.remoteCandidates), 0) assert.Equal(t, len(aAgent.localCandidates), 0) close(done) diff --git a/candidate_base.go b/candidate_base.go index dad95d2..499b872 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -267,7 +267,7 @@ func (c *candidateBase) handleInboundPacket(buf []byte, srcAddr net.Addr) { return } - if err := a.run(c, func(ctx context.Context, a *Agent) { + if err := a.run(c, func(_ context.Context, a *Agent) { // nolint: contextcheck a.handleInbound(m, c, srcAddr) }); err != nil { diff --git a/candidate_relay_test.go b/candidate_relay_test.go index b022671..59b89ab 100644 --- a/candidate_relay_test.go +++ b/candidate_relay_test.go @@ -31,7 +31,7 @@ func TestRelayOnlyConnection(t *testing.T) { defer report() serverPort := randomPort(t) - serverListener, err := net.ListenPacket("udp", "127.0.0.1:"+strconv.Itoa(serverPort)) + serverListener, err := net.ListenPacket("udp", localhostIPStr+":"+strconv.Itoa(serverPort)) assert.NoError(t, err) server, err := turn.NewServer(turn.ServerConfig{ @@ -40,7 +40,7 @@ func TestRelayOnlyConnection(t *testing.T) { PacketConnConfigs: []turn.PacketConnConfig{ { PacketConn: serverListener, - RelayAddressGenerator: &turn.RelayAddressGeneratorNone{Address: "127.0.0.1"}, + RelayAddressGenerator: &turn.RelayAddressGeneratorNone{Address: localhostIPStr + ""}, }, }, }) @@ -51,7 +51,7 @@ func TestRelayOnlyConnection(t *testing.T) { Urls: []*stun.URI{ { Scheme: stun.SchemeTypeTURN, - Host: "127.0.0.1", + Host: localhostIPStr + "", Username: "username", Password: "password", Port: serverPort, diff --git a/candidate_test.go b/candidate_test.go index 9fb7ebc..1cab463 100644 --- a/candidate_test.go +++ b/candidate_test.go @@ -358,14 +358,14 @@ func TestCandidateMarshal(t *testing.T) { candidateBase{ networkType: NetworkTypeUDP4, candidateType: CandidateTypeHost, - address: "127.0.0.1", + address: localhostIPStr, port: 80, priorityOverride: 500, foundationOverride: " ", }, "", }, - " 1 udp 500 127.0.0.1 80 typ host", + " 1 udp 500 " + localhostIPStr + " 80 typ host", false, }, diff --git a/connectivity_vnet_test.go b/connectivity_vnet_test.go index b70acee..0aadc95 100644 --- a/connectivity_vnet_test.go +++ b/connectivity_vnet_test.go @@ -171,7 +171,7 @@ func addVNetSTUN(wanNet *vnet.Net, loggerFactory logging.LoggerFactory) (*turn.S return nil, err } server, err := turn.NewServer(turn.ServerConfig{ - AuthHandler: func(username, realm string, srcAddr net.Addr) (key []byte, ok bool) { + AuthHandler: func(username, realm string, _ net.Addr) (key []byte, ok bool) { if pw, ok := credMap[username]; ok { return turn.GenerateAuthKey(username, realm, pw), true } diff --git a/gather.go b/gather.go index 8d2ce62..f39b55e 100644 --- a/gather.go +++ b/gather.go @@ -42,7 +42,7 @@ func closeConnAndLog(c io.Closer, log logging.LeveledLogger, msg string, args .. func (a *Agent) GatherCandidates() error { var gatherErr error - if runErr := a.run(a.context(), func(ctx context.Context, agent *Agent) { + if runErr := a.run(a.context(), func(ctx context.Context, _ *Agent) { if a.gatheringState != GatheringStateNew { gatherErr = ErrMultipleGatherAttempted return diff --git a/gather_test.go b/gather_test.go index 07e31c9..668891a 100644 --- a/gather_test.go +++ b/gather_test.go @@ -104,7 +104,7 @@ func TestGatherConcurrency(t *testing.T) { assert.NoError(t, err) candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) - assert.NoError(t, a.OnCandidate(func(c Candidate) { + assert.NoError(t, a.OnCandidate(func(Candidate) { candidateGatheredFunc() })) @@ -209,7 +209,7 @@ func TestSTUNConcurrency(t *testing.T) { defer lim.Stop() serverPort := randomPort(t) - serverListener, err := net.ListenPacket("udp4", "127.0.0.1:"+strconv.Itoa(serverPort)) + serverListener, err := net.ListenPacket("udp4", localhostIPStr+":"+strconv.Itoa(serverPort)) assert.NoError(t, err) server, err := turn.NewServer(turn.ServerConfig{ @@ -218,7 +218,7 @@ func TestSTUNConcurrency(t *testing.T) { PacketConnConfigs: []turn.PacketConnConfig{ { PacketConn: serverListener, - RelayAddressGenerator: &turn.RelayAddressGeneratorNone{Address: "127.0.0.1"}, + RelayAddressGenerator: &turn.RelayAddressGeneratorNone{Address: localhostIPStr}, }, }, }) @@ -228,13 +228,13 @@ func TestSTUNConcurrency(t *testing.T) { for i := 0; i <= 10; i++ { urls = append(urls, &stun.URI{ Scheme: stun.SchemeTypeSTUN, - Host: "127.0.0.1", + Host: localhostIPStr, Port: serverPort + 1, }) } urls = append(urls, &stun.URI{ Scheme: stun.SchemeTypeSTUN, - Host: "127.0.0.1", + Host: localhostIPStr, Port: serverPort, }) @@ -289,7 +289,7 @@ func TestTURNConcurrency(t *testing.T) { if packetConn != nil { packetConnConfigs = append(packetConnConfigs, turn.PacketConnConfig{ PacketConn: packetConn, - RelayAddressGenerator: &turn.RelayAddressGeneratorNone{Address: "127.0.0.1"}, + RelayAddressGenerator: &turn.RelayAddressGeneratorNone{Address: localhostIPStr}, }) } @@ -297,7 +297,7 @@ func TestTURNConcurrency(t *testing.T) { if listener != nil { listenerConfigs = append(listenerConfigs, turn.ListenerConfig{ Listener: listener, - RelayAddressGenerator: &turn.RelayAddressGeneratorNone{Address: "127.0.0.1"}, + RelayAddressGenerator: &turn.RelayAddressGeneratorNone{Address: localhostIPStr}, }) } @@ -313,7 +313,7 @@ func TestTURNConcurrency(t *testing.T) { for i := 0; i <= 10; i++ { urls = append(urls, &stun.URI{ Scheme: scheme, - Host: "127.0.0.1", + Host: localhostIPStr, Username: "username", Password: "password", Proto: protocol, @@ -322,7 +322,7 @@ func TestTURNConcurrency(t *testing.T) { } urls = append(urls, &stun.URI{ Scheme: scheme, - Host: "127.0.0.1", + Host: localhostIPStr, Username: "username", Password: "password", Proto: protocol, @@ -353,7 +353,7 @@ func TestTURNConcurrency(t *testing.T) { t.Run("UDP Relay", func(t *testing.T) { serverPort := randomPort(t) - serverListener, err := net.ListenPacket("udp", "127.0.0.1:"+strconv.Itoa(serverPort)) + serverListener, err := net.ListenPacket("udp", localhostIPStr+":"+strconv.Itoa(serverPort)) assert.NoError(t, err) runTest(stun.ProtoTypeUDP, stun.SchemeTypeTURN, serverListener, nil, serverPort) @@ -361,7 +361,7 @@ func TestTURNConcurrency(t *testing.T) { t.Run("TCP Relay", func(t *testing.T) { serverPort := randomPort(t) - serverListener, err := net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(serverPort)) + serverListener, err := net.Listen("tcp", localhostIPStr+":"+strconv.Itoa(serverPort)) assert.NoError(t, err) runTest(stun.ProtoTypeTCP, stun.SchemeTypeTURN, nil, serverListener, serverPort) @@ -372,7 +372,7 @@ func TestTURNConcurrency(t *testing.T) { assert.NoError(t, genErr) serverPort := randomPort(t) - serverListener, err := tls.Listen("tcp", "127.0.0.1:"+strconv.Itoa(serverPort), &tls.Config{ //nolint:gosec + serverListener, err := tls.Listen("tcp", localhostIPStr+":"+strconv.Itoa(serverPort), &tls.Config{ //nolint:gosec Certificates: []tls.Certificate{certificate}, }) assert.NoError(t, err) @@ -385,7 +385,7 @@ func TestTURNConcurrency(t *testing.T) { assert.NoError(t, genErr) serverPort := randomPort(t) - serverListener, err := dtls.Listen("udp", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: serverPort}, &dtls.Config{ + serverListener, err := dtls.Listen("udp", &net.UDPAddr{IP: net.ParseIP(localhostIPStr), Port: serverPort}, &dtls.Config{ Certificates: []tls.Certificate{certificate}, }) assert.NoError(t, err) @@ -403,7 +403,7 @@ func TestSTUNTURNConcurrency(t *testing.T) { defer lim.Stop() serverPort := randomPort(t) - serverListener, err := net.ListenPacket("udp4", "127.0.0.1:"+strconv.Itoa(serverPort)) + serverListener, err := net.ListenPacket("udp4", localhostIPStr+":"+strconv.Itoa(serverPort)) assert.NoError(t, err) server, err := turn.NewServer(turn.ServerConfig{ @@ -412,7 +412,7 @@ func TestSTUNTURNConcurrency(t *testing.T) { PacketConnConfigs: []turn.PacketConnConfig{ { PacketConn: serverListener, - RelayAddressGenerator: &turn.RelayAddressGeneratorNone{Address: "127.0.0.1"}, + RelayAddressGenerator: &turn.RelayAddressGeneratorNone{Address: localhostIPStr}, }, }, }) @@ -422,14 +422,14 @@ func TestSTUNTURNConcurrency(t *testing.T) { for i := 0; i <= 10; i++ { urls = append(urls, &stun.URI{ Scheme: stun.SchemeTypeSTUN, - Host: "127.0.0.1", + Host: localhostIPStr, Port: serverPort + 1, }) } urls = append(urls, &stun.URI{ Scheme: stun.SchemeTypeTURN, Proto: stun.ProtoTypeUDP, - Host: "127.0.0.1", + Host: localhostIPStr, Port: serverPort, Username: "username", Password: "password", @@ -475,7 +475,7 @@ func TestTURNSrflx(t *testing.T) { defer lim.Stop() serverPort := randomPort(t) - serverListener, err := net.ListenPacket("udp4", "127.0.0.1:"+strconv.Itoa(serverPort)) + serverListener, err := net.ListenPacket("udp4", localhostIPStr+":"+strconv.Itoa(serverPort)) assert.NoError(t, err) server, err := turn.NewServer(turn.ServerConfig{ @@ -484,7 +484,7 @@ func TestTURNSrflx(t *testing.T) { PacketConnConfigs: []turn.PacketConnConfig{ { PacketConn: serverListener, - RelayAddressGenerator: &turn.RelayAddressGeneratorNone{Address: "127.0.0.1"}, + RelayAddressGenerator: &turn.RelayAddressGeneratorNone{Address: localhostIPStr}, }, }, }) @@ -493,7 +493,7 @@ func TestTURNSrflx(t *testing.T) { urls := []*stun.URI{{ Scheme: stun.SchemeTypeTURN, Proto: stun.ProtoTypeUDP, - Host: "127.0.0.1", + Host: localhostIPStr, Port: serverPort, Username: "username", Password: "password", @@ -577,7 +577,7 @@ func TestTURNProxyDialer(t *testing.T) { Urls: []*stun.URI{ { Scheme: stun.SchemeTypeTURN, - Host: "127.0.0.1", + Host: localhostIPStr, Username: "username", Password: "password", Proto: stun.ProtoTypeTCP, @@ -787,7 +787,7 @@ func TestUniversalUDPMuxUsage(t *testing.T) { for i := 0; i < numSTUNS; i++ { urls = append(urls, &stun.URI{ Scheme: SchemeTypeSTUN, - Host: "127.0.0.1", + Host: localhostIPStr, Port: 3478 + i, }) } diff --git a/rand_test.go b/rand_test.go index e7e8569..4cb12b6 100644 --- a/rand_test.go +++ b/rand_test.go @@ -15,7 +15,7 @@ func TestRandomGeneratorCollision(t *testing.T) { gen func(t *testing.T) string }{ "CandidateID": { - gen: func(t *testing.T) string { + gen: func(*testing.T) string { return candidateIDGen.Generate() }, }, diff --git a/transport.go b/transport.go index d8b1a6e..9c30a82 100644 --- a/transport.go +++ b/transport.go @@ -91,7 +91,7 @@ func (c *Conn) Write(p []byte) (int, error) { pair := c.agent.getSelectedPair() if pair == nil { - if err = c.agent.run(c.agent.context(), func(ctx context.Context, a *Agent) { + if err = c.agent.run(c.agent.context(), func(_ context.Context, a *Agent) { pair = a.getBestValidCandidatePair() }); err != nil { return 0, err diff --git a/transport_test.go b/transport_test.go index e11ba37..5e967fd 100644 --- a/transport_test.go +++ b/transport_test.go @@ -49,7 +49,7 @@ func testTimeout(t *testing.T, c *Conn, timeout time.Duration) { var cs ConnectionState - err := c.agent.run(context.Background(), func(ctx context.Context, agent *Agent) { + err := c.agent.run(context.Background(), func(_ context.Context, agent *Agent) { cs = agent.connectionState }) if err != nil { From 26ba6dfea572cddc1707541ff7a12791564a55b6 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Mon, 18 Mar 2024 19:59:32 -0400 Subject: [PATCH 002/114] Fix WASM build Constant used by WASM+Go tests was in Go only file --- agent_test.go | 2 -- candidate_test.go | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agent_test.go b/agent_test.go index 8913b2b..f5ca54e 100644 --- a/agent_test.go +++ b/agent_test.go @@ -24,8 +24,6 @@ import ( "github.com/stretchr/testify/require" ) -const localhostIPStr = "127.0.0.1" - type BadAddr struct{} func (ba *BadAddr) Network() string { diff --git a/candidate_test.go b/candidate_test.go index 1cab463..5078154 100644 --- a/candidate_test.go +++ b/candidate_test.go @@ -13,6 +13,8 @@ import ( "github.com/stretchr/testify/require" ) +const localhostIPStr = "127.0.0.1" + func TestCandidateTypePreference(t *testing.T) { r := require.New(t) From 70eda83a498d7d9a3a8cf25e04bbe2bad41c209f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Mar 2024 00:03:17 +0000 Subject: [PATCH 003/114] Update module golang.org/x/net to v0.22.0 Generated by renovateBot --- go.mod | 2 +- go.sum | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 4f87ca6..3187ad8 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,6 @@ require ( github.com/pion/transport/v3 v3.0.1 github.com/pion/turn/v3 v3.0.1 github.com/stretchr/testify v1.8.4 - golang.org/x/net v0.20.0 + golang.org/x/net v0.22.0 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect ) diff --git a/go.sum b/go.sum index ede3fb2..f77ffe2 100644 --- a/go.sum +++ b/go.sum @@ -41,8 +41,10 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -52,8 +54,10 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -66,8 +70,10 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -75,6 +81,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= From f0c60fedf89b50892fe6cfd41c9e3c95d14f4505 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Mar 2024 00:07:22 +0000 Subject: [PATCH 004/114] Update module github.com/stretchr/testify to v1.9.0 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 3187ad8..76b3291 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pion/stun/v2 v2.0.0 github.com/pion/transport/v3 v3.0.1 github.com/pion/turn/v3 v3.0.1 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 golang.org/x/net v0.22.0 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect ) diff --git a/go.sum b/go.sum index f77ffe2..ab2cbed 100644 --- a/go.sum +++ b/go.sum @@ -31,11 +31,13 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= From 5fcf0380e71059600c0f459e33058c2c1198d890 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Tue, 19 Mar 2024 11:13:13 -0400 Subject: [PATCH 005/114] Update module github.com/pion/mdns to v2 Generated by renovateBot --- agent.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- mdns.go | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/agent.go b/agent.go index 6d671b2..c31d7ad 100644 --- a/agent.go +++ b/agent.go @@ -18,7 +18,7 @@ import ( atomicx "github.com/pion/ice/v3/internal/atomic" stunx "github.com/pion/ice/v3/internal/stun" "github.com/pion/logging" - "github.com/pion/mdns" + "github.com/pion/mdns/v2" "github.com/pion/stun/v2" "github.com/pion/transport/v3" "github.com/pion/transport/v3/packetio" diff --git a/go.mod b/go.mod index 76b3291..231f46c 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/kr/pretty v0.1.0 // indirect github.com/pion/dtls/v2 v2.2.10 github.com/pion/logging v0.2.2 - github.com/pion/mdns v0.0.12 + github.com/pion/mdns/v2 v2.0.4 github.com/pion/randutil v0.1.0 github.com/pion/stun/v2 v2.0.0 github.com/pion/transport/v3 v3.0.1 diff --git a/go.sum b/go.sum index ab2cbed..1e0706d 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,8 @@ github.com/pion/dtls/v2 v2.2.10 h1:u2Axk+FyIR1VFTPurktB+1zoEPGIW3bmyj3LEFrXjAA= github.com/pion/dtls/v2 v2.2.10/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= -github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8= -github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk= +github.com/pion/mdns/v2 v2.0.4 h1:ZdK19Yd+9iPrw95rW1tTwdjmYY5O3WwmsvfX6HBBefY= +github.com/pion/mdns/v2 v2.0.4/go.mod h1:y4Y034qALR23oAJuiElt2TP1ma7b1Q/uF1oYzIePHcM= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= diff --git a/mdns.go b/mdns.go index 1fa52a7..a3ad899 100644 --- a/mdns.go +++ b/mdns.go @@ -6,7 +6,7 @@ package ice import ( "github.com/google/uuid" "github.com/pion/logging" - "github.com/pion/mdns" + "github.com/pion/mdns/v2" "github.com/pion/transport/v3" "golang.org/x/net/ipv4" ) @@ -38,7 +38,7 @@ func createMulticastDNS(n transport.Net, mDNSMode MulticastDNSMode, mDNSName str return nil, mDNSMode, nil } - addr, mdnsErr := n.ResolveUDPAddr("udp4", mdns.DefaultAddress) + addr, mdnsErr := n.ResolveUDPAddr("udp4", mdns.DefaultAddressIPv4) if mdnsErr != nil { return nil, mDNSMode, mdnsErr } @@ -52,10 +52,10 @@ func createMulticastDNS(n transport.Net, mDNSMode MulticastDNSMode, mDNSName str switch mDNSMode { case MulticastDNSModeQueryOnly: - conn, err := mdns.Server(ipv4.NewPacketConn(l), &mdns.Config{}) + conn, err := mdns.Server(ipv4.NewPacketConn(l), nil, &mdns.Config{}) return conn, mDNSMode, err case MulticastDNSModeQueryAndGather: - conn, err := mdns.Server(ipv4.NewPacketConn(l), &mdns.Config{ + conn, err := mdns.Server(ipv4.NewPacketConn(l), nil, &mdns.Config{ LocalNames: []string{mDNSName}, }) return conn, mDNSMode, err From 970978e8c79a2d68234ff1e85b655dbde0b090c9 Mon Sep 17 00:00:00 2001 From: Dirk-Willem van Gulik Date: Sun, 18 Feb 2024 15:14:53 +0100 Subject: [PATCH 006/114] Start listening range at 1024 instead of 1 Avoid privileged ports by default Closes #651 --- net.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net.go b/net.go index 365745d..f686d41 100644 --- a/net.go +++ b/net.go @@ -106,7 +106,7 @@ func listenUDPInPortRange(n transport.Net, log logging.LeveledLogger, portMax, p var i, j int i = portMin if i == 0 { - i = 1 + i = 1024 // Start at 1024 which is non-privileged } j = portMax if j == 0 { From a72844663f279caa422e97ae760509d8916bc708 Mon Sep 17 00:00:00 2001 From: Aleks Todorov Date: Fri, 22 Dec 2023 11:08:01 +0000 Subject: [PATCH 007/114] Allow using mDNS candidates with UDP multiplexing --- gather.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/gather.go b/gather.go index f39b55e..c2bf40b 100644 --- a/gather.go +++ b/gather.go @@ -266,7 +266,9 @@ func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolin return errInvalidAddress } candidateIP := udpAddr.IP - if a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeHost { + if a.mDNSMode != MulticastDNSModeQueryAndGather && + a.extIPMapper != nil && + a.extIPMapper.candidateType == CandidateTypeHost { mappedIP, err := a.extIPMapper.findExternalIP(candidateIP.String()) if err != nil { a.log.Warnf("1:1 NAT mapping is enabled but no external IP is found for %s", candidateIP.String()) @@ -276,9 +278,16 @@ func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolin candidateIP = mappedIP } + var address string + if a.mDNSMode == MulticastDNSModeQueryAndGather { + address = a.mDNSName + } else { + address = candidateIP.String() + } + hostConfig := CandidateHostConfig{ Network: udp, - Address: candidateIP.String(), + Address: address, Port: udpAddr.Port, Component: ComponentRTP, } From 77cc354d7ff64da809e1b2b36b27ff443f7415ef Mon Sep 17 00:00:00 2001 From: dinvlad <137337+dinvlad@users.noreply.github.com> Date: Mon, 2 Oct 2023 13:13:40 -0400 Subject: [PATCH 008/114] Respect IncludeLoopback in UDPMuxDefault Currently, when using UDPMuxDefault with unspecified address, the loopback address is included by default, but agentConfig.IncludeLoopback is not respected when gathering local candidates. The same holds true when UDPMuxDefault is configured with a loopback address, but agentConfig.IncludeLoopback is not explicitly set to true. This commit adds an extra check to gatherCandidatesLocalUDPMux() for respecting that setting in both cases. --- agent_udpmux_test.go | 1 + gather.go | 7 +++++++ gather_test.go | 28 ++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/agent_udpmux_test.go b/agent_udpmux_test.go index 6050ccd..524795d 100644 --- a/agent_udpmux_test.go +++ b/agent_udpmux_test.go @@ -49,6 +49,7 @@ func TestMuxAgent(t *testing.T) { NetworkTypes: []NetworkType{ NetworkTypeUDP4, }, + IncludeLoopback: addr.IP.IsLoopback(), }) require.NoError(t, err) diff --git a/gather.go b/gather.go index c2bf40b..931d368 100644 --- a/gather.go +++ b/gather.go @@ -266,6 +266,13 @@ func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolin return errInvalidAddress } candidateIP := udpAddr.IP + + if _, ok := a.udpMux.(*UDPMuxDefault); ok && !a.includeLoopback && candidateIP.IsLoopback() { + // Unlike MultiUDPMux Default, UDPMuxDefault doesn't have + // a separate param to include loopback, so we respect agent config + continue + } + if a.mDNSMode != MulticastDNSModeQueryAndGather && a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeHost { diff --git a/gather_test.go b/gather_test.go index 668891a..8f31cd6 100644 --- a/gather_test.go +++ b/gather_test.go @@ -133,6 +133,16 @@ func TestLoopbackCandidate(t *testing.T) { assert.NoError(t, err) muxWithLo, errlo := NewMultiUDPMuxFromPort(12501, UDPMuxFromPortWithLoopback()) assert.NoError(t, errlo) + + unspecConn, errconn := net.ListenPacket("udp", ":0") + assert.NoError(t, errconn) + defer func() { + _ = unspecConn.Close() + }() + muxUnspecDefault := NewUDPMuxDefault(UDPMuxParams{ + UDPConn: unspecConn, + }) + testCases := []testCase{ { name: "mux should not have loopback candidate", @@ -150,6 +160,23 @@ func TestLoopbackCandidate(t *testing.T) { }, loExpected: true, }, + { + name: "UDPMuxDefault with unspecified IP should not have loopback candidate", + agentConfig: &AgentConfig{ + NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}, + UDPMux: muxUnspecDefault, + }, + loExpected: false, + }, + { + name: "UDPMuxDefault with unspecified IP should respect agent includeloopback", + agentConfig: &AgentConfig{ + NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}, + UDPMux: muxUnspecDefault, + IncludeLoopback: true, + }, + loExpected: true, + }, { name: "includeloopback enabled", agentConfig: &AgentConfig{ @@ -198,6 +225,7 @@ func TestLoopbackCandidate(t *testing.T) { assert.NoError(t, mux.Close()) assert.NoError(t, muxWithLo.Close()) + assert.NoError(t, muxUnspecDefault.Close()) } // Assert that STUN gathering is done concurrently From 67cc918a518d3b3dd1887b07434331dabc846702 Mon Sep 17 00:00:00 2001 From: sukun Date: Wed, 13 Mar 2024 21:25:57 +0530 Subject: [PATCH 009/114] Fix ConnectionState being reported out of order Before we launched a goroutine to announce every ConnectionState change to users. These could then be sent to the user out of order. This commit adds a connectionStateNotifier. The connectionStateNotifier delivers them sequentially to the user. Resolves #624 --- agent.go | 13 ++------ agent_handlers.go | 34 ++++++++++++++++++-- agent_handlers_test.go | 71 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 13 deletions(-) create mode 100644 agent_handlers_test.go diff --git a/agent.go b/agent.go index c31d7ad..afaec76 100644 --- a/agent.go +++ b/agent.go @@ -130,7 +130,7 @@ type Agent struct { chanCandidate chan Candidate chanCandidatePair chan *CandidatePair - chanState chan ConnectionState + stateNotifier *connectionStateNotifier loggerFactory logging.LoggerFactory log logging.LeveledLogger @@ -227,7 +227,6 @@ func (a *Agent) taskLoop() { after() - close(a.chanState) close(a.chanCandidate) close(a.chanCandidatePair) close(a.taskLoopDone) @@ -278,7 +277,6 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit a := &Agent{ chanTask: make(chan task), - chanState: make(chan ConnectionState), chanCandidate: make(chan Candidate), chanCandidatePair: make(chan *CandidatePair), tieBreaker: globalMathRandomGenerator.Uint64(), @@ -322,6 +320,7 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit disableActiveTCP: config.DisableActiveTCP, } + a.stateNotifier = &connectionStateNotifier{NotificationFunc: a.onConnectionStateChange} if a.net == nil { a.net, err = stdnet.NewNet() @@ -369,7 +368,6 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit // Blocking one by the other one causes deadlock. // Hence, we call handlers from independent Goroutines. go a.candidatePairRoutine() - go a.connectionStateRoutine() go a.candidateRoutine() // Restart is also used to initialize the agent for the first time @@ -503,12 +501,7 @@ func (a *Agent) updateConnectionState(newState ConnectionState) { a.log.Infof("Setting new connection state: %s", newState) a.connectionState = newState - - // Call handler after finishing current task since we may be holding the agent lock - // and the handler may also require it - a.afterRun(func(_ context.Context) { - a.chanState <- newState - }) + a.stateNotifier.Enqueue(newState) } } diff --git a/agent_handlers.go b/agent_handlers.go index c5a5ec0..4ec2484 100644 --- a/agent_handlers.go +++ b/agent_handlers.go @@ -3,6 +3,8 @@ package ice +import "sync" + // OnConnectionStateChange sets a handler that is fired when the connection state changes func (a *Agent) OnConnectionStateChange(f func(ConnectionState)) error { a.onConnectionStateChangeHdlr.Store(f) @@ -47,9 +49,35 @@ func (a *Agent) candidatePairRoutine() { } } -func (a *Agent) connectionStateRoutine() { - for s := range a.chanState { - go a.onConnectionStateChange(s) +type connectionStateNotifier struct { + sync.Mutex + states []ConnectionState + running bool + NotificationFunc func(ConnectionState) +} + +func (c *connectionStateNotifier) Enqueue(s ConnectionState) { + c.Lock() + defer c.Unlock() + c.states = append(c.states, s) + if !c.running { + c.running = true + go c.notify() + } +} + +func (c *connectionStateNotifier) notify() { + for { + c.Lock() + if len(c.states) == 0 { + c.running = false + c.Unlock() + return + } + s := c.states[0] + c.states = c.states[1:] + c.Unlock() + c.NotificationFunc(s) } } diff --git a/agent_handlers_test.go b/agent_handlers_test.go new file mode 100644 index 0000000..05ed5b8 --- /dev/null +++ b/agent_handlers_test.go @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package ice + +import ( + "testing" + "time" + + "github.com/pion/transport/v3/test" +) + +func TestConnectionStateNotifier(t *testing.T) { + t.Run("TestManyUpdates", func(t *testing.T) { + report := test.CheckRoutines(t) + defer report() + updates := make(chan struct{}, 1) + c := &connectionStateNotifier{ + NotificationFunc: func(_ ConnectionState) { + updates <- struct{}{} + }, + } + // Enqueue all updates upfront to ensure that it + // doesn't block + for i := 0; i < 10000; i++ { + c.Enqueue(ConnectionStateNew) + } + done := make(chan struct{}) + go func() { + for i := 0; i < 10000; i++ { + <-updates + } + select { + case <-updates: + t.Errorf("received more updates than expected") + case <-time.After(1 * time.Second): + } + close(done) + }() + <-done + }) + t.Run("TestUpdateOrdering", func(t *testing.T) { + report := test.CheckRoutines(t) + defer report() + updates := make(chan ConnectionState) + c := &connectionStateNotifier{ + NotificationFunc: func(cs ConnectionState) { + updates <- cs + }, + } + done := make(chan struct{}) + go func() { + for i := 0; i < 10000; i++ { + x := <-updates + if x != ConnectionState(i) { + t.Errorf("expected %d got %d", x, i) + } + } + select { + case <-updates: + t.Errorf("received more updates than expected") + case <-time.After(1 * time.Second): + } + close(done) + }() + for i := 0; i < 10000; i++ { + c.Enqueue(ConnectionState(i)) + } + <-done + }) +} From 8680cd591fd152081d91cd554fe0890fbf91641c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20M=C3=A9lan=C3=A7on?= Date: Wed, 22 Nov 2023 07:47:32 -0500 Subject: [PATCH 010/114] Match libwebrtc's TURN protocol priority Today, all relay candidates from Pion have the same priority. This PR attempts to reproduce libwebrtc's behavior, where the TURN servers candidates priority is based on the underlying relay protocol. UDP are preferred over TCP, which are preferred over the TLS options. --- candidate_relay.go | 17 +++++++++++++++++ gather.go | 4 ++-- ice.go | 5 +++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/candidate_relay.go b/candidate_relay.go index 449d077..fa5297b 100644 --- a/candidate_relay.go +++ b/candidate_relay.go @@ -70,6 +70,23 @@ func NewCandidateRelay(config *CandidateRelayConfig) (*CandidateRelay, error) { }, nil } +// LocalPreference returns the local preference for this candidate +func (c *CandidateRelay) LocalPreference() uint16 { + // These preference values come from libwebrtc + // https://github.com/mozilla/libwebrtc/blob/1389c76d9c79839a2ca069df1db48aa3f2e6a1ac/p2p/base/turn_port.cc#L61 + var relayPreference uint16 + switch c.relayProtocol { + case relayProtocolTLS, relayProtocolDTLS: + relayPreference = 2 + case tcp: + relayPreference = 1 + default: + relayPreference = 0 + } + + return c.candidateBase.LocalPreference() + relayPreference +} + // RelayProtocol returns the protocol used between the endpoint and the relay server. func (c *CandidateRelay) RelayProtocol() string { return c.relayProtocol diff --git a/gather.go b/gather.go index 931d368..507b7fc 100644 --- a/gather.go +++ b/gather.go @@ -632,7 +632,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / relAddr = conn.LocalAddr().(*net.UDPAddr).IP.String() //nolint:forcetypeassert relPort = conn.LocalAddr().(*net.UDPAddr).Port //nolint:forcetypeassert - relayProtocol = "dtls" + relayProtocol = relayProtocolDTLS locConn = &fakenet.PacketConn{Conn: conn} case url.Proto == stun.ProtoTypeTCP && url.Scheme == stun.SchemeTypeTURNS: tcpAddr, resolvErr := a.net.ResolveTCPAddr(NetworkTypeTCP4.String(), turnServerAddr) @@ -662,7 +662,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / relAddr = conn.LocalAddr().(*net.TCPAddr).IP.String() //nolint:forcetypeassert relPort = conn.LocalAddr().(*net.TCPAddr).Port //nolint:forcetypeassert - relayProtocol = "tls" + relayProtocol = relayProtocolTLS locConn = turn.NewSTUNConn(conn) default: a.log.Warnf("Unable to handle URL in gatherCandidatesRelay %v", url) diff --git a/ice.go b/ice.go index bd55120..73262dd 100644 --- a/ice.go +++ b/ice.go @@ -83,3 +83,8 @@ func (t GatheringState) String() string { return ErrUnknownType.Error() } } + +const ( + relayProtocolDTLS = "dtls" + relayProtocolTLS = "tls" +) From b386d4488fbf027f9ca37b09e259f3e32604db56 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Wed, 17 May 2023 10:12:36 +0200 Subject: [PATCH 011/114] Fix type of CandidatePairState constants --- candidatepair_state.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/candidatepair_state.go b/candidatepair_state.go index 1a1e827..b7f0dd8 100644 --- a/candidatepair_state.go +++ b/candidatepair_state.go @@ -9,7 +9,7 @@ type CandidatePairState int const ( // CandidatePairStateWaiting means a check has not been performed for // this pair - CandidatePairStateWaiting = iota + 1 + CandidatePairStateWaiting CandidatePairState = iota + 1 // CandidatePairStateInProgress means a check has been sent for this pair, // but the transaction is in progress. From d17be4df3b49c712adf6c2c9ab08a5c4c87676f5 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Thu, 21 Mar 2024 09:51:35 -0400 Subject: [PATCH 012/114] Use Notification Queue from 67cc918a518d more Deliver Candidates and Selected CandidatePairs using the same queue. This means that things are delivered in order and we don't have to worry about blocking --- agent.go | 83 +++++++++++++----------------- agent_handlers.go | 114 +++++++++++++++++++++++++++++------------ agent_handlers_test.go | 12 ++--- 3 files changed, 122 insertions(+), 87 deletions(-) diff --git a/agent.go b/agent.go index afaec76..7aac717 100644 --- a/agent.go +++ b/agent.go @@ -128,9 +128,9 @@ type Agent struct { gatherCandidateCancel func() gatherCandidateDone chan struct{} - chanCandidate chan Candidate - chanCandidatePair chan *CandidatePair - stateNotifier *connectionStateNotifier + connectionStateNotifier *handlerNotifier + candidateNotifier *handlerNotifier + selectedCandidatePairNotifier *handlerNotifier loggerFactory logging.LoggerFactory log logging.LeveledLogger @@ -227,8 +227,6 @@ func (a *Agent) taskLoop() { after() - close(a.chanCandidate) - close(a.chanCandidatePair) close(a.taskLoopDone) }() @@ -276,32 +274,30 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit startedCtx, startedFn := context.WithCancel(context.Background()) a := &Agent{ - chanTask: make(chan task), - chanCandidate: make(chan Candidate), - chanCandidatePair: make(chan *CandidatePair), - tieBreaker: globalMathRandomGenerator.Uint64(), - lite: config.Lite, - gatheringState: GatheringStateNew, - connectionState: ConnectionStateNew, - localCandidates: make(map[NetworkType][]Candidate), - remoteCandidates: make(map[NetworkType][]Candidate), - urls: config.Urls, - networkTypes: config.NetworkTypes, - onConnected: make(chan struct{}), - buf: packetio.NewBuffer(), - done: make(chan struct{}), - taskLoopDone: make(chan struct{}), - startedCh: startedCtx.Done(), - startedFn: startedFn, - portMin: config.PortMin, - portMax: config.PortMax, - loggerFactory: loggerFactory, - log: log, - net: config.Net, - proxyDialer: config.ProxyDialer, - tcpMux: config.TCPMux, - udpMux: config.UDPMux, - udpMuxSrflx: config.UDPMuxSrflx, + chanTask: make(chan task), + tieBreaker: globalMathRandomGenerator.Uint64(), + lite: config.Lite, + gatheringState: GatheringStateNew, + connectionState: ConnectionStateNew, + localCandidates: make(map[NetworkType][]Candidate), + remoteCandidates: make(map[NetworkType][]Candidate), + urls: config.Urls, + networkTypes: config.NetworkTypes, + onConnected: make(chan struct{}), + buf: packetio.NewBuffer(), + done: make(chan struct{}), + taskLoopDone: make(chan struct{}), + startedCh: startedCtx.Done(), + startedFn: startedFn, + portMin: config.PortMin, + portMax: config.PortMax, + loggerFactory: loggerFactory, + log: log, + net: config.Net, + proxyDialer: config.ProxyDialer, + tcpMux: config.TCPMux, + udpMux: config.UDPMux, + udpMuxSrflx: config.UDPMuxSrflx, mDNSMode: mDNSMode, mDNSName: mDNSName, @@ -320,7 +316,9 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit disableActiveTCP: config.DisableActiveTCP, } - a.stateNotifier = &connectionStateNotifier{NotificationFunc: a.onConnectionStateChange} + a.connectionStateNotifier = &handlerNotifier{connectionStateFunc: a.onConnectionStateChange} + a.candidateNotifier = &handlerNotifier{candidateFunc: a.onCandidate} + a.selectedCandidatePairNotifier = &handlerNotifier{candidatePairFunc: a.onSelectedCandidatePairChange} if a.net == nil { a.net, err = stdnet.NewNet() @@ -364,12 +362,6 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit go a.taskLoop() - // CandidatePair and ConnectionState are usually changed at once. - // Blocking one by the other one causes deadlock. - // Hence, we call handlers from independent Goroutines. - go a.candidatePairRoutine() - go a.candidateRoutine() - // Restart is also used to initialize the agent for the first time if err := a.Restart(config.LocalUfrag, config.LocalPwd); err != nil { a.closeMulticastConn() @@ -501,7 +493,7 @@ func (a *Agent) updateConnectionState(newState ConnectionState) { a.log.Infof("Setting new connection state: %s", newState) a.connectionState = newState - a.stateNotifier.Enqueue(newState) + a.connectionStateNotifier.EnqueueConnectionState(newState) } } @@ -520,12 +512,7 @@ func (a *Agent) setSelectedPair(p *CandidatePair) { a.updateConnectionState(ConnectionStateConnected) // Notify when the selected pair changes - a.afterRun(func(ctx context.Context) { - select { - case a.chanCandidatePair <- p: - case <-ctx.Done(): - } - }) + a.selectedCandidatePairNotifier.EnqueueSelectedCandidatePair(p) // Signal connected a.onConnectedOnce.Do(func() { close(a.onConnected) }) @@ -761,7 +748,7 @@ func (a *Agent) addRemotePassiveTCPCandidate(remoteCandidate Candidate) { localCandidate.start(a, conn, a.startedCh) a.localCandidates[localCandidate.NetworkType()] = append(a.localCandidates[localCandidate.NetworkType()], localCandidate) - a.chanCandidate <- localCandidate + a.candidateNotifier.EnqueueCandidate(localCandidate) a.addPair(localCandidate, remoteCandidate) } @@ -831,7 +818,7 @@ func (a *Agent) addCandidate(ctx context.Context, c Candidate, candidateConn net a.requestConnectivityCheck() - a.chanCandidate <- c + a.candidateNotifier.EnqueueCandidate(c) }) } @@ -1267,7 +1254,7 @@ func (a *Agent) setGatheringState(newState GatheringState) error { done := make(chan struct{}) if err := a.run(a.context(), func(context.Context, *Agent) { if a.gatheringState != newState && newState == GatheringStateComplete { - a.chanCandidate <- nil + a.candidateNotifier.EnqueueCandidate(nil) } a.gatheringState = newState diff --git a/agent_handlers.go b/agent_handlers.go index 4ec2484..bb0c8d3 100644 --- a/agent_handlers.go +++ b/agent_handlers.go @@ -43,46 +43,94 @@ func (a *Agent) onConnectionStateChange(s ConnectionState) { } } -func (a *Agent) candidatePairRoutine() { - for p := range a.chanCandidatePair { - a.onSelectedCandidatePairChange(p) - } -} - -type connectionStateNotifier struct { +type handlerNotifier struct { sync.Mutex - states []ConnectionState - running bool - NotificationFunc func(ConnectionState) + running bool + + connectionStates []ConnectionState + connectionStateFunc func(ConnectionState) + + candidates []Candidate + candidateFunc func(Candidate) + + selectedCandidatePairs []*CandidatePair + candidatePairFunc func(*CandidatePair) } -func (c *connectionStateNotifier) Enqueue(s ConnectionState) { - c.Lock() - defer c.Unlock() - c.states = append(c.states, s) - if !c.running { - c.running = true - go c.notify() - } -} +func (h *handlerNotifier) EnqueueConnectionState(s ConnectionState) { + h.Lock() + defer h.Unlock() -func (c *connectionStateNotifier) notify() { - for { - c.Lock() - if len(c.states) == 0 { - c.running = false - c.Unlock() - return + notify := func() { + for { + h.Lock() + if len(h.connectionStates) == 0 { + h.running = false + h.Unlock() + return + } + notification := h.connectionStates[0] + h.connectionStates = h.connectionStates[1:] + h.Unlock() + h.connectionStateFunc(notification) } - s := c.states[0] - c.states = c.states[1:] - c.Unlock() - c.NotificationFunc(s) + } + + h.connectionStates = append(h.connectionStates, s) + if !h.running { + h.running = true + go notify() } } -func (a *Agent) candidateRoutine() { - for c := range a.chanCandidate { - a.onCandidate(c) +func (h *handlerNotifier) EnqueueCandidate(c Candidate) { + h.Lock() + defer h.Unlock() + + notify := func() { + for { + h.Lock() + if len(h.candidates) == 0 { + h.running = false + h.Unlock() + return + } + notification := h.candidates[0] + h.candidates = h.candidates[1:] + h.Unlock() + h.candidateFunc(notification) + } + } + + h.candidates = append(h.candidates, c) + if !h.running { + h.running = true + go notify() + } +} + +func (h *handlerNotifier) EnqueueSelectedCandidatePair(p *CandidatePair) { + h.Lock() + defer h.Unlock() + + notify := func() { + for { + h.Lock() + if len(h.selectedCandidatePairs) == 0 { + h.running = false + h.Unlock() + return + } + notification := h.selectedCandidatePairs[0] + h.selectedCandidatePairs = h.selectedCandidatePairs[1:] + h.Unlock() + h.candidatePairFunc(notification) + } + } + + h.selectedCandidatePairs = append(h.selectedCandidatePairs, p) + if !h.running { + h.running = true + go notify() } } diff --git a/agent_handlers_test.go b/agent_handlers_test.go index 05ed5b8..675f853 100644 --- a/agent_handlers_test.go +++ b/agent_handlers_test.go @@ -15,15 +15,15 @@ func TestConnectionStateNotifier(t *testing.T) { report := test.CheckRoutines(t) defer report() updates := make(chan struct{}, 1) - c := &connectionStateNotifier{ - NotificationFunc: func(_ ConnectionState) { + c := &handlerNotifier{ + connectionStateFunc: func(_ ConnectionState) { updates <- struct{}{} }, } // Enqueue all updates upfront to ensure that it // doesn't block for i := 0; i < 10000; i++ { - c.Enqueue(ConnectionStateNew) + c.EnqueueConnectionState(ConnectionStateNew) } done := make(chan struct{}) go func() { @@ -43,8 +43,8 @@ func TestConnectionStateNotifier(t *testing.T) { report := test.CheckRoutines(t) defer report() updates := make(chan ConnectionState) - c := &connectionStateNotifier{ - NotificationFunc: func(cs ConnectionState) { + c := &handlerNotifier{ + connectionStateFunc: func(cs ConnectionState) { updates <- cs }, } @@ -64,7 +64,7 @@ func TestConnectionStateNotifier(t *testing.T) { close(done) }() for i := 0; i < 10000; i++ { - c.Enqueue(ConnectionState(i)) + c.EnqueueConnectionState(ConnectionState(i)) } <-done }) From b36d33253b09a4b5ef23e8f0a49cb25cd7eafa5e Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Thu, 21 Mar 2024 10:55:09 -0400 Subject: [PATCH 013/114] Remove afterRun from Task Loop Not needed anymore since we have Notification Queues --- agent.go | 43 +++++-------------------------------------- 1 file changed, 5 insertions(+), 38 deletions(-) diff --git a/agent.go b/agent.go index 7aac717..44e8823 100644 --- a/agent.go +++ b/agent.go @@ -36,9 +36,7 @@ type bindingRequest struct { // Agent represents the ICE agent type Agent struct { - chanTask chan task - afterRunFn []func(ctx context.Context) - muAfterRun sync.Mutex + chanTask chan task onConnectionStateChangeHdlr atomic.Value // func(ConnectionState) onSelectedCandidatePairChangeHdlr atomic.Value // func(Candidate, Candidate) @@ -154,21 +152,6 @@ type task struct { done chan struct{} } -// afterRun registers function to be run after the task. -func (a *Agent) afterRun(f func(context.Context)) { - a.muAfterRun.Lock() - a.afterRunFn = append(a.afterRunFn, f) - a.muAfterRun.Unlock() -} - -func (a *Agent) getAfterRunFn() []func(context.Context) { - a.muAfterRun.Lock() - defer a.muAfterRun.Unlock() - fns := a.afterRunFn - a.afterRunFn = nil - return fns -} - func (a *Agent) ok() error { select { case <-a.done: @@ -202,18 +185,6 @@ func (a *Agent) run(ctx context.Context, t func(context.Context, *Agent)) error // taskLoop handles registered tasks and agent close. func (a *Agent) taskLoop() { - after := func() { - for { - // Get and run func registered by afterRun(). - fns := a.getAfterRunFn() - if len(fns) == 0 { - break - } - for _, fn := range fns { - fn(a.context()) - } - } - } defer func() { a.deleteAllCandidates() a.startedFn() @@ -225,7 +196,10 @@ func (a *Agent) taskLoop() { a.closeMulticastConn() a.updateConnectionState(ConnectionStateClosed) - after() + a.gatherCandidateCancel() + if a.gatherCandidateDone != nil { + <-a.gatherCandidateDone + } close(a.taskLoopDone) }() @@ -237,7 +211,6 @@ func (a *Agent) taskLoop() { case t := <-a.chanTask: t.fn(a.context(), a) close(t.done) - after() } } } @@ -906,12 +879,6 @@ func (a *Agent) Close() error { return err } - a.afterRun(func(context.Context) { - a.gatherCandidateCancel() - if a.gatherCandidateDone != nil { - <-a.gatherCandidateDone - } - }) a.err.Store(ErrClosed) a.removeUfragFromMux() From fdca6c47c021ded52a993e05f5fea5598ffa7016 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Thu, 21 Mar 2024 11:47:51 -0400 Subject: [PATCH 014/114] Move taskloop into dedicated package Reduce size of Agent and simplify code --- agent.go | 181 ++++++------------ ..._on_selected_candidate_pair_change_test.go | 2 +- agent_stats.go | 18 +- agent_test.go | 56 +++--- candidate_base.go | 2 +- context.go | 40 ---- gather.go | 4 +- internal/taskloop/taskloop.go | 121 ++++++++++++ transport.go | 14 +- transport_test.go | 4 +- 10 files changed, 227 insertions(+), 215 deletions(-) delete mode 100644 context.go create mode 100644 internal/taskloop/taskloop.go diff --git a/agent.go b/agent.go index 44e8823..27d429d 100644 --- a/agent.go +++ b/agent.go @@ -15,8 +15,8 @@ import ( "sync/atomic" "time" - atomicx "github.com/pion/ice/v3/internal/atomic" stunx "github.com/pion/ice/v3/internal/stun" + "github.com/pion/ice/v3/internal/taskloop" "github.com/pion/logging" "github.com/pion/mdns/v2" "github.com/pion/stun/v2" @@ -36,13 +36,12 @@ type bindingRequest struct { // Agent represents the ICE agent type Agent struct { - chanTask chan task + loop *taskloop.Loop onConnectionStateChangeHdlr atomic.Value // func(ConnectionState) onSelectedCandidatePairChangeHdlr atomic.Value // func(Candidate, Candidate) onCandidateHdlr atomic.Value // func(Candidate) - // State owned by the taskLoop onConnected chan struct{} onConnectedOnce sync.Once @@ -118,11 +117,6 @@ type Agent struct { // 1:1 D-NAT IP address mapping extIPMapper *externalIPMapper - // State for closing - done chan struct{} - taskLoopDone chan struct{} - err atomicx.Error - gatherCandidateCancel func() gatherCandidateDone chan struct{} @@ -147,74 +141,6 @@ type Agent struct { proxyDialer proxy.Dialer } -type task struct { - fn func(context.Context, *Agent) - done chan struct{} -} - -func (a *Agent) ok() error { - select { - case <-a.done: - return a.getErr() - default: - } - return nil -} - -func (a *Agent) getErr() error { - if err := a.err.Load(); err != nil { - return err - } - return ErrClosed -} - -// Run task in serial. Blocking tasks must be cancelable by context. -func (a *Agent) run(ctx context.Context, t func(context.Context, *Agent)) error { - if err := a.ok(); err != nil { - return err - } - done := make(chan struct{}) - select { - case <-ctx.Done(): - return ctx.Err() - case a.chanTask <- task{t, done}: - <-done - return nil - } -} - -// taskLoop handles registered tasks and agent close. -func (a *Agent) taskLoop() { - defer func() { - a.deleteAllCandidates() - a.startedFn() - - if err := a.buf.Close(); err != nil { - a.log.Warnf("Failed to close buffer: %v", err) - } - - a.closeMulticastConn() - a.updateConnectionState(ConnectionStateClosed) - - a.gatherCandidateCancel() - if a.gatherCandidateDone != nil { - <-a.gatherCandidateDone - } - - close(a.taskLoopDone) - }() - - for { - select { - case <-a.done: - return - case t := <-a.chanTask: - t.fn(a.context(), a) - close(t.done) - } - } -} - // NewAgent creates a new Agent func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit var err error @@ -247,7 +173,6 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit startedCtx, startedFn := context.WithCancel(context.Background()) a := &Agent{ - chanTask: make(chan task), tieBreaker: globalMathRandomGenerator.Uint64(), lite: config.Lite, gatheringState: GatheringStateNew, @@ -258,8 +183,6 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit networkTypes: config.NetworkTypes, onConnected: make(chan struct{}), buf: packetio.NewBuffer(), - done: make(chan struct{}), - taskLoopDone: make(chan struct{}), startedCh: startedCtx.Done(), startedFn: startedFn, portMin: config.PortMin, @@ -333,7 +256,23 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit return nil, err } - go a.taskLoop() + a.loop = taskloop.New(func() { + a.removeUfragFromMux() + a.deleteAllCandidates() + a.startedFn() + + if err := a.buf.Close(); err != nil { + a.log.Warnf("Failed to close buffer: %v", err) + } + + a.closeMulticastConn() + a.updateConnectionState(ConnectionStateClosed) + + a.gatherCandidateCancel() + if a.gatherCandidateDone != nil { + <-a.gatherCandidateDone + } + }) // Restart is also used to initialize the agent for the first time if err := a.Restart(config.LocalUfrag, config.LocalPwd); err != nil { @@ -359,10 +298,10 @@ func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remoteP a.log.Debugf("Started agent: isControlling? %t, remoteUfrag: %q, remotePwd: %q", isControlling, remoteUfrag, remotePwd) - return a.run(a.context(), func(_ context.Context, agent *Agent) { - agent.isControlling = isControlling - agent.remoteUfrag = remoteUfrag - agent.remotePwd = remotePwd + return a.loop.Run(a.loop, func(_ context.Context) { + a.isControlling = isControlling + a.remoteUfrag = remoteUfrag + a.remotePwd = remotePwd if isControlling { a.selector = &controllingSelector{agent: a, log: a.log} @@ -377,7 +316,7 @@ func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remoteP a.selector.Start() a.startedFn() - agent.updateConnectionState(ConnectionStateChecking) + a.updateConnectionState(ConnectionStateChecking) a.requestConnectivityCheck() go a.connectivityChecks() //nolint:contextcheck @@ -389,7 +328,7 @@ func (a *Agent) connectivityChecks() { checkingDuration := time.Time{} contact := func() { - if err := a.run(a.context(), func(_ context.Context, a *Agent) { + if err := a.loop.Run(a.loop, func(_ context.Context) { defer func() { lastConnectionState = a.connectionState }() @@ -446,7 +385,7 @@ func (a *Agent) connectivityChecks() { contact() case <-t.C: contact() - case <-a.done: + case <-a.loop.Done(): t.Stop() return } @@ -638,9 +577,9 @@ func (a *Agent) AddRemoteCandidate(c Candidate) error { } go func() { - if err := a.run(a.context(), func(_ context.Context, agent *Agent) { + if err := a.loop.Run(a.loop, func(_ context.Context) { // nolint: contextcheck - agent.addRemoteCandidate(c) + a.addRemoteCandidate(c) }); err != nil { a.log.Warnf("Failed to add remote candidate %s: %v", c.Address(), err) return @@ -670,9 +609,9 @@ func (a *Agent) resolveAndAddMulticastCandidate(c *CandidateHost) { return } - if err = a.run(a.context(), func(_ context.Context, agent *Agent) { + if err = a.loop.Run(a.loop, func(_ context.Context) { // nolint: contextcheck - agent.addRemoteCandidate(c) + a.addRemoteCandidate(c) }); err != nil { a.log.Warnf("Failed to add mDNS candidate %s: %v", c.Address(), err) return @@ -695,7 +634,7 @@ func (a *Agent) addRemotePassiveTCPCandidate(remoteCandidate Candidate) { for i := range localIPs { conn := newActiveTCPConn( - a.context(), + a.loop, net.JoinHostPort(localIPs[i].String(), "0"), net.JoinHostPort(remoteCandidate.Address(), strconv.Itoa(remoteCandidate.Port())), a.log, @@ -763,7 +702,7 @@ func (a *Agent) addRemoteCandidate(c Candidate) { } func (a *Agent) addCandidate(ctx context.Context, c Candidate, candidateConn net.PacketConn) error { - return a.run(ctx, func(context.Context, *Agent) { + return a.loop.Run(ctx, func(context.Context) { set := a.localCandidates[c.NetworkType()] for _, candidate := range set { if candidate.Equal(c) { @@ -799,9 +738,9 @@ func (a *Agent) addCandidate(ctx context.Context, c Candidate, candidateConn net func (a *Agent) GetRemoteCandidates() ([]Candidate, error) { var res []Candidate - err := a.run(a.context(), func(_ context.Context, agent *Agent) { + err := a.loop.Run(a.loop, func(_ context.Context) { var candidates []Candidate - for _, set := range agent.remoteCandidates { + for _, set := range a.remoteCandidates { candidates = append(candidates, set...) } res = candidates @@ -817,9 +756,9 @@ func (a *Agent) GetRemoteCandidates() ([]Candidate, error) { func (a *Agent) GetLocalCandidates() ([]Candidate, error) { var res []Candidate - err := a.run(a.context(), func(_ context.Context, agent *Agent) { + err := a.loop.Run(a.loop, func(_ context.Context) { var candidates []Candidate - for _, set := range agent.localCandidates { + for _, set := range a.localCandidates { candidates = append(candidates, set...) } res = candidates @@ -834,9 +773,9 @@ func (a *Agent) GetLocalCandidates() ([]Candidate, error) { // GetLocalUserCredentials returns the local user credentials func (a *Agent) GetLocalUserCredentials() (frag string, pwd string, err error) { valSet := make(chan struct{}) - err = a.run(a.context(), func(_ context.Context, agent *Agent) { - frag = agent.localUfrag - pwd = agent.localPwd + err = a.loop.Run(a.loop, func(_ context.Context) { + frag = a.localUfrag + pwd = a.localPwd close(valSet) }) @@ -849,9 +788,9 @@ func (a *Agent) GetLocalUserCredentials() (frag string, pwd string, err error) { // GetRemoteUserCredentials returns the remote user credentials func (a *Agent) GetRemoteUserCredentials() (frag string, pwd string, err error) { valSet := make(chan struct{}) - err = a.run(a.context(), func(_ context.Context, agent *Agent) { - frag = agent.remoteUfrag - pwd = agent.remotePwd + err = a.loop.Run(a.loop, func(_ context.Context) { + frag = a.remoteUfrag + pwd = a.remotePwd close(valSet) }) @@ -875,17 +814,7 @@ func (a *Agent) removeUfragFromMux() { // Close cleans up the Agent func (a *Agent) Close() error { - if err := a.ok(); err != nil { - return err - } - - a.err.Store(ErrClosed) - - a.removeUfragFromMux() - - close(a.done) - <-a.taskLoopDone - return nil + return a.loop.Close() } // Remove all candidates. This closes any listening sockets @@ -1092,7 +1021,7 @@ func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr) // and returns true if it is an actual remote candidate func (a *Agent) validateNonSTUNTraffic(local Candidate, remote net.Addr) (Candidate, bool) { var remoteCandidate Candidate - if err := a.run(local.context(), func(context.Context, *Agent) { + if err := a.loop.Run(local.context(), func(context.Context) { remoteCandidate = a.findRemoteCandidate(local.NetworkType(), remote) if remoteCandidate != nil { remoteCandidate.seen(false) @@ -1149,9 +1078,9 @@ func (a *Agent) SetRemoteCredentials(remoteUfrag, remotePwd string) error { return ErrRemotePwdEmpty } - return a.run(a.context(), func(_ context.Context, agent *Agent) { - agent.remoteUfrag = remoteUfrag - agent.remotePwd = remotePwd + return a.loop.Run(a.loop, func(_ context.Context) { + a.remoteUfrag = remoteUfrag + a.remotePwd = remotePwd }) } @@ -1186,17 +1115,17 @@ func (a *Agent) Restart(ufrag, pwd string) error { } var err error - if runErr := a.run(a.context(), func(_ context.Context, agent *Agent) { - if agent.gatheringState == GatheringStateGathering { - agent.gatherCandidateCancel() + if runErr := a.loop.Run(a.loop, func(_ context.Context) { + if a.gatheringState == GatheringStateGathering { + a.gatherCandidateCancel() } // Clear all agent needed to take back to fresh state a.removeUfragFromMux() - agent.localUfrag = ufrag - agent.localPwd = pwd - agent.remoteUfrag = "" - agent.remotePwd = "" + a.localUfrag = ufrag + a.localPwd = pwd + a.remoteUfrag = "" + a.remotePwd = "" a.gatheringState = GatheringStateNew a.checklist = make([]*CandidatePair, 0) a.pendingBindingRequests = make([]bindingRequest, 0) @@ -1219,7 +1148,7 @@ func (a *Agent) Restart(ufrag, pwd string) error { func (a *Agent) setGatheringState(newState GatheringState) error { done := make(chan struct{}) - if err := a.run(a.context(), func(context.Context, *Agent) { + if err := a.loop.Run(a.loop, func(context.Context) { if a.gatheringState != newState && newState == GatheringStateComplete { a.candidateNotifier.EnqueueCandidate(nil) } diff --git a/agent_on_selected_candidate_pair_change_test.go b/agent_on_selected_candidate_pair_change_test.go index 63b35ed..b744749 100644 --- a/agent_on_selected_candidate_pair_change_test.go +++ b/agent_on_selected_candidate_pair_change_test.go @@ -22,7 +22,7 @@ func TestOnSelectedCandidatePairChange(t *testing.T) { }) require.NoError(t, err) - err = agent.run(context.Background(), func(_ context.Context, agent *Agent) { + err = agent.loop.Run(context.Background(), func(_ context.Context) { agent.setSelectedPair(candidatePair) }) require.NoError(t, err) diff --git a/agent_stats.go b/agent_stats.go index 9582cac..035c652 100644 --- a/agent_stats.go +++ b/agent_stats.go @@ -11,9 +11,9 @@ import ( // GetCandidatePairsStats returns a list of candidate pair stats func (a *Agent) GetCandidatePairsStats() []CandidatePairStats { var res []CandidatePairStats - err := a.run(a.context(), func(_ context.Context, agent *Agent) { - result := make([]CandidatePairStats, 0, len(agent.checklist)) - for _, cp := range agent.checklist { + err := a.loop.Run(a.loop, func(_ context.Context) { + result := make([]CandidatePairStats, 0, len(a.checklist)) + for _, cp := range a.checklist { stat := CandidatePairStats{ Timestamp: time.Now(), LocalCandidateID: cp.Local.ID(), @@ -57,9 +57,9 @@ func (a *Agent) GetCandidatePairsStats() []CandidatePairStats { // GetLocalCandidatesStats returns a list of local candidates stats func (a *Agent) GetLocalCandidatesStats() []CandidateStats { var res []CandidateStats - err := a.run(a.context(), func(_ context.Context, agent *Agent) { - result := make([]CandidateStats, 0, len(agent.localCandidates)) - for networkType, localCandidates := range agent.localCandidates { + err := a.loop.Run(a.loop, func(_ context.Context) { + result := make([]CandidateStats, 0, len(a.localCandidates)) + for networkType, localCandidates := range a.localCandidates { for _, c := range localCandidates { relayProtocol := "" if c.Type() == CandidateTypeRelay { @@ -94,9 +94,9 @@ func (a *Agent) GetLocalCandidatesStats() []CandidateStats { // GetRemoteCandidatesStats returns a list of remote candidates stats func (a *Agent) GetRemoteCandidatesStats() []CandidateStats { var res []CandidateStats - err := a.run(a.context(), func(_ context.Context, agent *Agent) { - result := make([]CandidateStats, 0, len(agent.remoteCandidates)) - for networkType, remoteCandidates := range agent.remoteCandidates { + err := a.loop.Run(a.loop, func(_ context.Context) { + result := make([]CandidateStats, 0, len(a.remoteCandidates)) + for networkType, remoteCandidates := range a.remoteCandidates { for _, c := range remoteCandidates { stat := CandidateStats{ Timestamp: time.Now(), diff --git a/agent_test.go b/agent_test.go index f5ca54e..5cbb93a 100644 --- a/agent_test.go +++ b/agent_test.go @@ -34,19 +34,6 @@ func (ba *BadAddr) String() string { return "yyy" } -func runAgentTest(t *testing.T, config *AgentConfig, task func(ctx context.Context, a *Agent)) { - a, err := NewAgent(config) - if err != nil { - t.Fatalf("Error constructing ice.Agent") - } - - if err := a.run(context.Background(), task); err != nil { - t.Fatalf("Agent run failure: %v", err) - } - - assert.NoError(t, a.Close()) -} - func TestHandlePeerReflexive(t *testing.T) { report := test.CheckRoutines(t) defer report() @@ -56,8 +43,10 @@ func TestHandlePeerReflexive(t *testing.T) { defer lim.Stop() t.Run("UDP prflx candidate from handleInbound()", func(t *testing.T) { - var config AgentConfig - runAgentTest(t, &config, func(_ context.Context, a *Agent) { + a, err := NewAgent(&AgentConfig{}) + assert.NoError(t, err) + + assert.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { a.selector = &controllingSelector{agent: a, log: a.log} hostConfig := CandidateHostConfig{ @@ -113,12 +102,15 @@ func TestHandlePeerReflexive(t *testing.T) { if c.Port() != 999 { t.Fatal("Port number mismatch") } - }) + })) + assert.NoError(t, a.Close()) }) t.Run("Bad network type with handleInbound()", func(t *testing.T) { - var config AgentConfig - runAgentTest(t, &config, func(_ context.Context, a *Agent) { + a, err := NewAgent(&AgentConfig{}) + assert.NoError(t, err) + + assert.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { a.selector = &controllingSelector{agent: a, log: a.log} hostConfig := CandidateHostConfig{ @@ -140,12 +132,16 @@ func TestHandlePeerReflexive(t *testing.T) { if len(a.remoteCandidates) != 0 { t.Fatal("bad address should not be added to the remote candidate list") } - }) + })) + + assert.NoError(t, a.Close()) }) t.Run("Success from unknown remote, prflx candidate MUST only be created via Binding Request", func(t *testing.T) { - var config AgentConfig - runAgentTest(t, &config, func(_ context.Context, a *Agent) { + a, err := NewAgent(&AgentConfig{}) + assert.NoError(t, err) + + assert.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { a.selector = &controllingSelector{agent: a, log: a.log} tID := [stun.TransactionIDSize]byte{} copy(tID[:], "ABC") @@ -179,7 +175,9 @@ func TestHandlePeerReflexive(t *testing.T) { if len(a.remoteCandidates) != 0 { t.Fatal("unknown remote was able to create a candidate") } - }) + })) + + assert.NoError(t, a.Close()) }) } @@ -440,7 +438,7 @@ func TestInboundValidity(t *testing.T) { t.Fatalf("Error constructing ice.Agent") } - err = a.run(context.Background(), func(_ context.Context, a *Agent) { + err = a.loop.Run(a.loop, func(_ context.Context) { a.selector = &controllingSelector{agent: a, log: a.log} // nolint: contextcheck a.handleInbound(buildMsg(stun.ClassRequest, a.localUfrag+":"+a.remoteUfrag, a.localPwd), local, remote) @@ -454,8 +452,10 @@ func TestInboundValidity(t *testing.T) { }) t.Run("Valid bind without fingerprint", func(t *testing.T) { - var config AgentConfig - runAgentTest(t, &config, func(_ context.Context, a *Agent) { + a, err := NewAgent(&AgentConfig{}) + assert.NoError(t, err) + + assert.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { a.selector = &controllingSelector{agent: a, log: a.log} msg, err := stun.Build(stun.BindingRequest, stun.TransactionID, stun.NewUsername(a.localUfrag+":"+a.remoteUfrag), @@ -470,7 +470,9 @@ func TestInboundValidity(t *testing.T) { if len(a.remoteCandidates) != 1 { t.Fatal("Binding with valid values (but no fingerprint) was unable to create prflx candidate") } - }) + })) + + assert.NoError(t, a.Close()) }) t.Run("Success with invalid TransactionID", func(t *testing.T) { @@ -1120,7 +1122,7 @@ func TestConnectionStateFailedDeleteAllCandidates(t *testing.T) { <-isFailed done := make(chan struct{}) - assert.NoError(t, aAgent.run(context.Background(), func(context.Context, *Agent) { + assert.NoError(t, aAgent.loop.Run(aAgent.loop, func(context.Context) { assert.Equal(t, len(aAgent.remoteCandidates), 0) assert.Equal(t, len(aAgent.localCandidates), 0) close(done) diff --git a/candidate_base.go b/candidate_base.go index 499b872..fd0f292 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -267,7 +267,7 @@ func (c *candidateBase) handleInboundPacket(buf []byte, srcAddr net.Addr) { return } - if err := a.run(c, func(_ context.Context, a *Agent) { + if err := a.loop.Run(c, func(_ context.Context) { // nolint: contextcheck a.handleInbound(m, c, srcAddr) }); err != nil { diff --git a/context.go b/context.go deleted file mode 100644 index 3645445..0000000 --- a/context.go +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The Pion community -// SPDX-License-Identifier: MIT - -package ice - -import ( - "context" - "time" -) - -func (a *Agent) context() context.Context { - return agentContext(a.done) -} - -type agentContext chan struct{} - -// Done implements context.Context -func (a agentContext) Done() <-chan struct{} { - return (chan struct{})(a) -} - -// Err implements context.Context -func (a agentContext) Err() error { - select { - case <-(chan struct{})(a): - return ErrRunCanceled - default: - return nil - } -} - -// Deadline implements context.Context -func (a agentContext) Deadline() (deadline time.Time, ok bool) { - return time.Time{}, false -} - -// Value implements context.Context -func (a agentContext) Value(interface{}) interface{} { - return nil -} diff --git a/gather.go b/gather.go index 507b7fc..258e7fc 100644 --- a/gather.go +++ b/gather.go @@ -42,7 +42,7 @@ func closeConnAndLog(c io.Closer, log logging.LeveledLogger, msg string, args .. func (a *Agent) GatherCandidates() error { var gatherErr error - if runErr := a.run(a.context(), func(ctx context.Context, _ *Agent) { + if runErr := a.loop.Run(a.loop, func(ctx context.Context) { if a.gatheringState != GatheringStateNew { gatherErr = ErrMultipleGatherAttempted return @@ -495,7 +495,7 @@ func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, net select { case <-cancelCtx.Done(): return - case <-a.done: + case <-a.loop.Done(): _ = conn.Close() } }() diff --git a/internal/taskloop/taskloop.go b/internal/taskloop/taskloop.go new file mode 100644 index 0000000..2e55dc3 --- /dev/null +++ b/internal/taskloop/taskloop.go @@ -0,0 +1,121 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package taskloop implements a task loop to run +// tasks sequentially in a separate Goroutine. +package taskloop + +import ( + "context" + "errors" + "time" + + atomicx "github.com/pion/ice/v3/internal/atomic" +) + +// errClosed indicates that the loop has been stopped +var errClosed = errors.New("the agent is closed") + +type task struct { + fn func(context.Context) + done chan struct{} +} + +// Loop runs submitted task serially in a dedicated Goroutine +type Loop struct { + tasks chan task + + // State for closing + done chan struct{} + taskLoopDone chan struct{} + err atomicx.Error +} + +// New creates and starts a new task loop +func New(onClose func()) *Loop { + l := &Loop{ + tasks: make(chan task), + done: make(chan struct{}), + taskLoopDone: make(chan struct{}), + } + + go l.runLoop(onClose) + return l +} + +// runLoop handles registered tasks and agent close. +func (l *Loop) runLoop(onClose func()) { + defer func() { + onClose() + close(l.taskLoopDone) + }() + + for { + select { + case <-l.done: + return + case t := <-l.tasks: + t.fn(l) + close(t.done) + } + } +} + +// Close stops the loop after finishing the execution of the current task. +// Other pending tasks will not be executed. +func (l *Loop) Close() error { + if err := l.Err(); err != nil { + return err + } + + l.err.Store(errClosed) + + close(l.done) + <-l.taskLoopDone + + return nil +} + +// Run serially executes the submitted callback. +// Blocking tasks must be cancelable by context. +func (l *Loop) Run(ctx context.Context, t func(context.Context)) error { + if err := l.Err(); err != nil { + return err + } + done := make(chan struct{}) + select { + case <-ctx.Done(): + return ctx.Err() + case l.tasks <- task{t, done}: + <-done + return nil + } +} + +// The following methods implement context.Context for TaskLoop + +// Done returns a channel that's closed when the task loop has been stopped. +func (l *Loop) Done() <-chan struct{} { + return l.done +} + +// Err returns nil if the task loop is still running. +// Otherwise it return errClosed if the loop has been closed/stopped. +func (l *Loop) Err() error { + select { + case <-l.done: + return errClosed + default: + return nil + } +} + +// Deadline returns the no valid time as task loops have no deadline. +func (l *Loop) Deadline() (deadline time.Time, ok bool) { + return time.Time{}, false +} + +// Value is not supported for task loops +func (l *Loop) Value(interface{}) interface{} { + return nil +} diff --git a/transport.go b/transport.go index 9c30a82..f800152 100644 --- a/transport.go +++ b/transport.go @@ -43,7 +43,7 @@ func (c *Conn) BytesReceived() uint64 { } func (a *Agent) connect(ctx context.Context, isControlling bool, remoteUfrag, remotePwd string) (*Conn, error) { - err := a.ok() + err := a.loop.Err() if err != nil { return nil, err } @@ -54,8 +54,8 @@ func (a *Agent) connect(ctx context.Context, isControlling bool, remoteUfrag, re // Block until pair selected select { - case <-a.done: - return nil, a.getErr() + case <-a.loop.Done(): + return nil, a.loop.Err() case <-ctx.Done(): return nil, ErrCanceledByCaller case <-a.onConnected: @@ -68,7 +68,7 @@ func (a *Agent) connect(ctx context.Context, isControlling bool, remoteUfrag, re // Read implements the Conn Read method. func (c *Conn) Read(p []byte) (int, error) { - err := c.agent.ok() + err := c.agent.loop.Err() if err != nil { return 0, err } @@ -80,7 +80,7 @@ func (c *Conn) Read(p []byte) (int, error) { // Write implements the Conn Write method. func (c *Conn) Write(p []byte) (int, error) { - err := c.agent.ok() + err := c.agent.loop.Err() if err != nil { return 0, err } @@ -91,8 +91,8 @@ func (c *Conn) Write(p []byte) (int, error) { pair := c.agent.getSelectedPair() if pair == nil { - if err = c.agent.run(c.agent.context(), func(_ context.Context, a *Agent) { - pair = a.getBestValidCandidatePair() + if err = c.agent.loop.Run(c.agent.loop, func(_ context.Context) { + pair = c.agent.getBestValidCandidatePair() }); err != nil { return 0, err } diff --git a/transport_test.go b/transport_test.go index 5e967fd..9f0abd1 100644 --- a/transport_test.go +++ b/transport_test.go @@ -49,8 +49,8 @@ func testTimeout(t *testing.T, c *Conn, timeout time.Duration) { var cs ConnectionState - err := c.agent.run(context.Background(), func(_ context.Context, agent *Agent) { - cs = agent.connectionState + err := c.agent.loop.Run(context.Background(), func(_ context.Context) { + cs = c.agent.connectionState }) if err != nil { // We should never get here. From 05ab6847411265463a1852a0de4d597f1d9d99bc Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Fri, 22 Mar 2024 21:46:47 -0400 Subject: [PATCH 015/114] Use testify/require instead of testify/assert Don't continue to run a test if it has already failed --- active_tcp_test.go | 9 +- agent_get_best_valid_candidate_pair_test.go | 3 +- agent_test.go | 299 ++++++++++---------- candidate_relay_test.go | 12 +- candidate_server_reflexive_test.go | 12 +- candidate_test.go | 31 +- candidatepair_test.go | 4 +- connectivity_vnet_test.go | 106 +++---- external_ip_mapper_test.go | 198 ++++++------- gather_test.go | 201 +++++++------ gather_vnet_test.go | 84 +++--- ice_test.go | 6 +- mdns_test.go | 20 +- net_test.go | 10 +- networktype_test.go | 18 +- tcp_mux_multi_test.go | 15 +- tcp_mux_test.go | 21 +- tcptype_test.go | 22 +- transport_vnet_test.go | 34 ++- 19 files changed, 533 insertions(+), 572 deletions(-) diff --git a/active_tcp_test.go b/active_tcp_test.go index e2f2752..69d73e9 100644 --- a/active_tcp_test.go +++ b/active_tcp_test.go @@ -14,7 +14,6 @@ import ( "github.com/pion/logging" "github.com/pion/transport/v3/stdnet" "github.com/pion/transport/v3/test" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -199,12 +198,12 @@ func TestActiveTCP_NonBlocking(t *testing.T) { if err != nil { t.Fatal(err) } - assert.NoError(t, aAgent.AddRemoteCandidate(invalidCandidate)) - assert.NoError(t, bAgent.AddRemoteCandidate(invalidCandidate)) + require.NoError(t, aAgent.AddRemoteCandidate(invalidCandidate)) + require.NoError(t, bAgent.AddRemoteCandidate(invalidCandidate)) connect(aAgent, bAgent) <-isConnected - assert.NoError(t, aAgent.Close()) - assert.NoError(t, bAgent.Close()) + require.NoError(t, aAgent.Close()) + require.NoError(t, bAgent.Close()) } diff --git a/agent_get_best_valid_candidate_pair_test.go b/agent_get_best_valid_candidate_pair_test.go index a3c551a..24e3c47 100644 --- a/agent_get_best_valid_candidate_pair_test.go +++ b/agent_get_best_valid_candidate_pair_test.go @@ -9,7 +9,6 @@ package ice import ( "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -28,7 +27,7 @@ func TestAgentGetBestValidCandidatePair(t *testing.T) { require.Equal(t, actualBestPair.String(), expectedBestPair.String()) } - assert.NoError(t, f.sut.Close()) + require.NoError(t, f.sut.Close()) } func setupTestAgentGetBestValidCandidatePair(t *testing.T) *TestAgentGetBestValidCandidatePairFixture { diff --git a/agent_test.go b/agent_test.go index 5cbb93a..851070f 100644 --- a/agent_test.go +++ b/agent_test.go @@ -20,7 +20,6 @@ import ( "github.com/pion/stun/v2" "github.com/pion/transport/v3/test" "github.com/pion/transport/v3/vnet" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -44,9 +43,9 @@ func TestHandlePeerReflexive(t *testing.T) { t.Run("UDP prflx candidate from handleInbound()", func(t *testing.T) { a, err := NewAgent(&AgentConfig{}) - assert.NoError(t, err) + require.NoError(t, err) - assert.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { + require.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { a.selector = &controllingSelector{agent: a, log: a.log} hostConfig := CandidateHostConfig{ @@ -103,14 +102,14 @@ func TestHandlePeerReflexive(t *testing.T) { t.Fatal("Port number mismatch") } })) - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) }) t.Run("Bad network type with handleInbound()", func(t *testing.T) { a, err := NewAgent(&AgentConfig{}) - assert.NoError(t, err) + require.NoError(t, err) - assert.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { + require.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { a.selector = &controllingSelector{agent: a, log: a.log} hostConfig := CandidateHostConfig{ @@ -134,14 +133,14 @@ func TestHandlePeerReflexive(t *testing.T) { } })) - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) }) t.Run("Success from unknown remote, prflx candidate MUST only be created via Binding Request", func(t *testing.T) { a, err := NewAgent(&AgentConfig{}) - assert.NoError(t, err) + require.NoError(t, err) - assert.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { + require.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { a.selector = &controllingSelector{agent: a, log: a.log} tID := [stun.TransactionIDSize]byte{} copy(tID[:], "ABC") @@ -177,7 +176,7 @@ func TestHandlePeerReflexive(t *testing.T) { } })) - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) }) } @@ -195,21 +194,21 @@ func TestConnectivityOnStartup(t *testing.T) { CIDR: "0.0.0.0/0", LoggerFactory: logging.NewDefaultLoggerFactory(), }) - assert.NoError(t, err) + require.NoError(t, err) net0, err := vnet.NewNet(&vnet.NetConfig{ StaticIPs: []string{"192.168.0.1"}, }) - assert.NoError(t, err) - assert.NoError(t, wan.AddNet(net0)) + require.NoError(t, err) + require.NoError(t, wan.AddNet(net0)) net1, err := vnet.NewNet(&vnet.NetConfig{ StaticIPs: []string{"192.168.0.2"}, }) - assert.NoError(t, err) - assert.NoError(t, wan.AddNet(net1)) + require.NoError(t, err) + require.NoError(t, wan.AddNet(net1)) - assert.NoError(t, wan.Start()) + require.NoError(t, wan.Start()) aNotifier, aConnected := onConnected() bNotifier, bConnected := onConnected() @@ -242,10 +241,10 @@ func TestConnectivityOnStartup(t *testing.T) { aConn, bConn := func(aAgent, bAgent *Agent) (*Conn, *Conn) { // Manual signaling aUfrag, aPwd, err := aAgent.GetLocalUserCredentials() - assert.NoError(t, err) + require.NoError(t, err) bUfrag, bPwd, err := bAgent.GetLocalUserCredentials() - assert.NoError(t, err) + require.NoError(t, err) gatherAndExchangeCandidates(aAgent, bAgent) @@ -288,10 +287,8 @@ func TestConnectivityOnStartup(t *testing.T) { <-aConnected <-bConnected - assert.NoError(t, wan.Stop()) - if !closePipe(t, aConn, bConn) { - return - } + require.NoError(t, wan.Stop()) + closePipe(t, aConn, bConn) } func TestConnectivityLite(t *testing.T) { @@ -350,9 +347,7 @@ func TestConnectivityLite(t *testing.T) { <-aConnected <-bConnected - if !closePipe(t, aConn, bConn) { - return - } + closePipe(t, aConn, bConn) } func TestInboundValidity(t *testing.T) { @@ -401,7 +396,7 @@ func TestInboundValidity(t *testing.T) { t.Fatal("Binding with invalid MessageIntegrity was able to create prflx candidate") } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) }) t.Run("Invalid Binding success responses should be discarded", func(t *testing.T) { @@ -415,7 +410,7 @@ func TestInboundValidity(t *testing.T) { t.Fatal("Binding with invalid MessageIntegrity was able to create prflx candidate") } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) }) t.Run("Discard non-binding messages", func(t *testing.T) { @@ -429,7 +424,7 @@ func TestInboundValidity(t *testing.T) { t.Fatal("non-binding message was able to create prflxRemote") } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) }) t.Run("Valid bind request", func(t *testing.T) { @@ -447,15 +442,15 @@ func TestInboundValidity(t *testing.T) { } }) - assert.NoError(t, err) - assert.NoError(t, a.Close()) + require.NoError(t, err) + require.NoError(t, a.Close()) }) t.Run("Valid bind without fingerprint", func(t *testing.T) { a, err := NewAgent(&AgentConfig{}) - assert.NoError(t, err) + require.NoError(t, err) - assert.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { + require.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { a.selector = &controllingSelector{agent: a, log: a.log} msg, err := stun.Build(stun.BindingRequest, stun.TransactionID, stun.NewUsername(a.localUfrag+":"+a.remoteUfrag), @@ -472,7 +467,7 @@ func TestInboundValidity(t *testing.T) { } })) - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) }) t.Run("Success with invalid TransactionID", func(t *testing.T) { @@ -500,14 +495,14 @@ func TestInboundValidity(t *testing.T) { stun.NewShortTermIntegrity(a.remotePwd), stun.Fingerprint, ) - assert.NoError(t, err) + require.NoError(t, err) a.handleInbound(msg, local, remote) if len(a.remoteCandidates) != 0 { t.Fatal("unknown remote was able to create a candidate") } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) }) } @@ -516,7 +511,7 @@ func TestInvalidAgentStarts(t *testing.T) { defer report() a, err := NewAgent(&AgentConfig{}) - assert.NoError(t, err) + require.NoError(t, err) ctx := context.Background() ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) @@ -538,7 +533,7 @@ func TestInvalidAgentStarts(t *testing.T) { t.Fatal(err) } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) } // Assert that Agent emits Connecting/Connected/Disconnected/Failed/Closed messages @@ -602,8 +597,8 @@ func TestConnectionStateCallback(t *testing.T) { <-isDisconnected <-isFailed - assert.NoError(t, aAgent.Close()) - assert.NoError(t, bAgent.Close()) + require.NoError(t, aAgent.Close()) + require.NoError(t, bAgent.Close()) <-isClosed } @@ -619,7 +614,7 @@ func TestInvalidGather(t *testing.T) { if !errors.Is(err, ErrNoOnCandidateHandler) { t.Fatal("trickle GatherCandidates succeeded without OnCandidate") } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) }) } @@ -753,7 +748,7 @@ func TestCandidatePairStats(t *testing.T) { prflxPairStat.State.String()) } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) } func TestLocalCandidateStats(t *testing.T) { @@ -834,7 +829,7 @@ func TestLocalCandidateStats(t *testing.T) { t.Fatal("missing srflx local stat") } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) } func TestRemoteCandidateStats(t *testing.T) { @@ -954,7 +949,7 @@ func TestRemoteCandidateStats(t *testing.T) { t.Fatal("missing host remote stat") } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) } func TestInitExtIPMapping(t *testing.T) { @@ -969,7 +964,7 @@ func TestInitExtIPMapping(t *testing.T) { if a.extIPMapper != nil { t.Fatal("a.extIPMapper should be nil by default") } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) // a.extIPMapper should be nil when NAT1To1IPs is a non-nil empty array a, err = NewAgent(&AgentConfig{ @@ -982,7 +977,7 @@ func TestInitExtIPMapping(t *testing.T) { if a.extIPMapper != nil { t.Fatal("a.extIPMapper should be nil by default") } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) // NewAgent should return an error when 1:1 NAT for host candidate is enabled // but the candidate type does not appear in the CandidateTypes. @@ -1034,7 +1029,7 @@ func TestBindingRequestTimeout(t *testing.T) { const expectedRemovalCount = 2 a, err := NewAgent(&AgentConfig{}) - assert.NoError(t, err) + require.NoError(t, err) now := time.Now() a.pendingBindingRequests = append(a.pendingBindingRequests, bindingRequest{ @@ -1051,8 +1046,8 @@ func TestBindingRequestTimeout(t *testing.T) { }) a.invalidatePendingBindingRequests(now) - assert.Equal(t, expectedRemovalCount, len(a.pendingBindingRequests), "Binding invalidation due to timeout did not remove the correct number of binding requests") - assert.NoError(t, a.Close()) + require.Equal(t, expectedRemovalCount, len(a.pendingBindingRequests), "Binding invalidation due to timeout did not remove the correct number of binding requests") + require.NoError(t, a.Close()) } // TestAgentCredentials checks if local username fragments and passwords (if set) meet RFC standard @@ -1069,10 +1064,10 @@ func TestAgentCredentials(t *testing.T) { // If set, they should follow the default 16/128 bits random number generator strategy agent, err := NewAgent(&AgentConfig{LoggerFactory: log}) - assert.NoError(t, err) - assert.GreaterOrEqual(t, len([]rune(agent.localUfrag))*8, 24) - assert.GreaterOrEqual(t, len([]rune(agent.localPwd))*8, 128) - assert.NoError(t, agent.Close()) + require.NoError(t, err) + require.GreaterOrEqual(t, len([]rune(agent.localUfrag))*8, 24) + require.GreaterOrEqual(t, len([]rune(agent.localPwd))*8, 128) + require.NoError(t, agent.Close()) // Should honor RFC standards // Local values MUST be unguessable, with at least 128 bits of @@ -1080,10 +1075,10 @@ func TestAgentCredentials(t *testing.T) { // at least 24 bits of output to generate the username fragment. _, err = NewAgent(&AgentConfig{LocalUfrag: "xx", LoggerFactory: log}) - assert.EqualError(t, err, ErrLocalUfragInsufficientBits.Error()) + require.EqualError(t, err, ErrLocalUfragInsufficientBits.Error()) _, err = NewAgent(&AgentConfig{LocalPwd: "xxxxxx", LoggerFactory: log}) - assert.EqualError(t, err, ErrLocalPwdInsufficientBits.Error()) + require.EqualError(t, err, ErrLocalPwdInsufficientBits.Error()) } // Assert that Agent on Failure deletes all existing candidates @@ -1106,13 +1101,13 @@ func TestConnectionStateFailedDeleteAllCandidates(t *testing.T) { } aAgent, err := NewAgent(cfg) - assert.NoError(t, err) + require.NoError(t, err) bAgent, err := NewAgent(cfg) - assert.NoError(t, err) + require.NoError(t, err) isFailed := make(chan interface{}) - assert.NoError(t, aAgent.OnConnectionStateChange(func(c ConnectionState) { + require.NoError(t, aAgent.OnConnectionStateChange(func(c ConnectionState) { if c == ConnectionStateFailed { close(isFailed) } @@ -1122,15 +1117,15 @@ func TestConnectionStateFailedDeleteAllCandidates(t *testing.T) { <-isFailed done := make(chan struct{}) - assert.NoError(t, aAgent.loop.Run(aAgent.loop, func(context.Context) { - assert.Equal(t, len(aAgent.remoteCandidates), 0) - assert.Equal(t, len(aAgent.localCandidates), 0) + require.NoError(t, aAgent.loop.Run(context.Background(), func(context.Context) { + require.Equal(t, len(aAgent.remoteCandidates), 0) + require.Equal(t, len(aAgent.localCandidates), 0) close(done) })) <-done - assert.NoError(t, aAgent.Close()) - assert.NoError(t, bAgent.Close()) + require.NoError(t, aAgent.Close()) + require.NoError(t, bAgent.Close()) } // Assert that the ICE Agent can go directly from Connecting -> Failed on both sides @@ -1151,10 +1146,10 @@ func TestConnectionStateConnectingToFailed(t *testing.T) { } aAgent, err := NewAgent(cfg) - assert.NoError(t, err) + require.NoError(t, err) bAgent, err := NewAgent(cfg) - assert.NoError(t, err) + require.NoError(t, err) var isFailed sync.WaitGroup var isChecking sync.WaitGroup @@ -1174,24 +1169,24 @@ func TestConnectionStateConnectingToFailed(t *testing.T) { } } - assert.NoError(t, aAgent.OnConnectionStateChange(connectionStateCheck)) - assert.NoError(t, bAgent.OnConnectionStateChange(connectionStateCheck)) + require.NoError(t, aAgent.OnConnectionStateChange(connectionStateCheck)) + require.NoError(t, bAgent.OnConnectionStateChange(connectionStateCheck)) go func() { _, err := aAgent.Accept(context.TODO(), "InvalidFrag", "InvalidPwd") - assert.Error(t, err) + require.Error(t, err) }() go func() { _, err := bAgent.Dial(context.TODO(), "InvalidFrag", "InvalidPwd") - assert.Error(t, err) + require.Error(t, err) }() isChecking.Wait() isFailed.Wait() - assert.NoError(t, aAgent.Close()) - assert.NoError(t, bAgent.Close()) + require.NoError(t, aAgent.Close()) + require.NoError(t, bAgent.Close()) } func TestAgentRestart(t *testing.T) { @@ -1210,26 +1205,26 @@ func TestAgentRestart(t *testing.T) { }) ctx, cancel := context.WithCancel(context.Background()) - assert.NoError(t, connB.agent.OnConnectionStateChange(func(c ConnectionState) { + require.NoError(t, connB.agent.OnConnectionStateChange(func(c ConnectionState) { if c == ConnectionStateFailed || c == ConnectionStateDisconnected { cancel() } })) connA.agent.gatheringState = GatheringStateGathering - assert.NoError(t, connA.agent.Restart("", "")) + require.NoError(t, connA.agent.Restart("", "")) <-ctx.Done() - assert.NoError(t, connA.agent.Close()) - assert.NoError(t, connB.agent.Close()) + require.NoError(t, connA.agent.Close()) + require.NoError(t, connB.agent.Close()) }) t.Run("Restart When Closed", func(t *testing.T) { agent, err := NewAgent(&AgentConfig{}) - assert.NoError(t, err) - assert.NoError(t, agent.Close()) + require.NoError(t, err) + require.NoError(t, agent.Close()) - assert.Equal(t, ErrClosed, agent.Restart("", "")) + require.Equal(t, ErrClosed, agent.Restart("", "")) }) t.Run("Restart One Side", func(t *testing.T) { @@ -1239,22 +1234,22 @@ func TestAgentRestart(t *testing.T) { }) ctx, cancel := context.WithCancel(context.Background()) - assert.NoError(t, connB.agent.OnConnectionStateChange(func(c ConnectionState) { + require.NoError(t, connB.agent.OnConnectionStateChange(func(c ConnectionState) { if c == ConnectionStateFailed || c == ConnectionStateDisconnected { cancel() } })) - assert.NoError(t, connA.agent.Restart("", "")) + require.NoError(t, connA.agent.Restart("", "")) <-ctx.Done() - assert.NoError(t, connA.agent.Close()) - assert.NoError(t, connB.agent.Close()) + require.NoError(t, connA.agent.Close()) + require.NoError(t, connB.agent.Close()) }) t.Run("Restart Both Sides", func(t *testing.T) { // Get all addresses of candidates concatenated generateCandidateAddressStrings := func(candidates []Candidate, err error) (out string) { - assert.NoError(t, err) + require.NoError(t, err) for _, c := range candidates { out += c.Address() + ":" @@ -1272,23 +1267,23 @@ func TestAgentRestart(t *testing.T) { connBFirstCandidates := generateCandidateAddressStrings(connB.agent.GetLocalCandidates()) aNotifier, aConnected := onConnected() - assert.NoError(t, connA.agent.OnConnectionStateChange(aNotifier)) + require.NoError(t, connA.agent.OnConnectionStateChange(aNotifier)) bNotifier, bConnected := onConnected() - assert.NoError(t, connB.agent.OnConnectionStateChange(bNotifier)) + require.NoError(t, connB.agent.OnConnectionStateChange(bNotifier)) // Restart and Re-Signal - assert.NoError(t, connA.agent.Restart("", "")) - assert.NoError(t, connB.agent.Restart("", "")) + require.NoError(t, connA.agent.Restart("", "")) + require.NoError(t, connB.agent.Restart("", "")) // Exchange Candidates and Credentials ufrag, pwd, err := connB.agent.GetLocalUserCredentials() - assert.NoError(t, err) - assert.NoError(t, connA.agent.SetRemoteCredentials(ufrag, pwd)) + require.NoError(t, err) + require.NoError(t, connA.agent.SetRemoteCredentials(ufrag, pwd)) ufrag, pwd, err = connA.agent.GetLocalUserCredentials() - assert.NoError(t, err) - assert.NoError(t, connB.agent.SetRemoteCredentials(ufrag, pwd)) + require.NoError(t, err) + require.NoError(t, connB.agent.SetRemoteCredentials(ufrag, pwd)) gatherAndExchangeCandidates(connA.agent, connB.agent) @@ -1297,11 +1292,11 @@ func TestAgentRestart(t *testing.T) { <-bConnected // Assert that we have new candidates each time - assert.NotEqual(t, connAFirstCandidates, generateCandidateAddressStrings(connA.agent.GetLocalCandidates())) - assert.NotEqual(t, connBFirstCandidates, generateCandidateAddressStrings(connB.agent.GetLocalCandidates())) + require.NotEqual(t, connAFirstCandidates, generateCandidateAddressStrings(connA.agent.GetLocalCandidates())) + require.NotEqual(t, connBFirstCandidates, generateCandidateAddressStrings(connB.agent.GetLocalCandidates())) - assert.NoError(t, connA.agent.Close()) - assert.NoError(t, connB.agent.Close()) + require.NoError(t, connA.agent.Close()) + require.NoError(t, connB.agent.Close()) }) } @@ -1316,12 +1311,12 @@ func TestGetRemoteCredentials(t *testing.T) { a.remotePwd = "remotePwd" actualUfrag, actualPwd, err := a.GetRemoteUserCredentials() - assert.NoError(t, err) + require.NoError(t, err) - assert.Equal(t, actualUfrag, a.remoteUfrag) - assert.Equal(t, actualPwd, a.remotePwd) + require.Equal(t, actualUfrag, a.remoteUfrag) + require.Equal(t, actualPwd, a.remotePwd) - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) } func TestGetRemoteCandidates(t *testing.T) { @@ -1343,7 +1338,7 @@ func TestGetRemoteCandidates(t *testing.T) { } cand, errCand := NewCandidateHost(&cfg) - assert.NoError(t, errCand) + require.NoError(t, errCand) expectedCandidates = append(expectedCandidates, cand) @@ -1351,10 +1346,10 @@ func TestGetRemoteCandidates(t *testing.T) { } actualCandidates, err := a.GetRemoteCandidates() - assert.NoError(t, err) - assert.ElementsMatch(t, expectedCandidates, actualCandidates) + require.NoError(t, err) + require.ElementsMatch(t, expectedCandidates, actualCandidates) - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) } func TestGetLocalCandidates(t *testing.T) { @@ -1377,19 +1372,19 @@ func TestGetLocalCandidates(t *testing.T) { } cand, errCand := NewCandidateHost(&cfg) - assert.NoError(t, errCand) + require.NoError(t, errCand) expectedCandidates = append(expectedCandidates, cand) err = a.addCandidate(context.Background(), cand, dummyConn) - assert.NoError(t, err) + require.NoError(t, err) } actualCandidates, err := a.GetLocalCandidates() - assert.NoError(t, err) - assert.ElementsMatch(t, expectedCandidates, actualCandidates) + require.NoError(t, err) + require.ElementsMatch(t, expectedCandidates, actualCandidates) - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) } func TestCloseInConnectionStateCallback(t *testing.T) { @@ -1429,7 +1424,7 @@ func TestCloseInConnectionStateCallback(t *testing.T) { switch c { case ConnectionStateConnected: <-isConnected - assert.NoError(t, aAgent.Close()) + require.NoError(t, aAgent.Close()) case ConnectionStateClosed: close(isClosed) default: @@ -1443,7 +1438,7 @@ func TestCloseInConnectionStateCallback(t *testing.T) { close(isConnected) <-isClosed - assert.NoError(t, bAgent.Close()) + require.NoError(t, bAgent.Close()) } func TestRunTaskInConnectionStateCallback(t *testing.T) { @@ -1475,8 +1470,8 @@ func TestRunTaskInConnectionStateCallback(t *testing.T) { err = aAgent.OnConnectionStateChange(func(c ConnectionState) { if c == ConnectionStateConnected { _, _, errCred := aAgent.GetLocalUserCredentials() - assert.NoError(t, errCred) - assert.NoError(t, aAgent.Restart("", "")) + require.NoError(t, errCred) + require.NoError(t, aAgent.Restart("", "")) close(isComplete) } }) @@ -1487,8 +1482,8 @@ func TestRunTaskInConnectionStateCallback(t *testing.T) { connect(aAgent, bAgent) <-isComplete - assert.NoError(t, aAgent.Close()) - assert.NoError(t, bAgent.Close()) + require.NoError(t, aAgent.Close()) + require.NoError(t, bAgent.Close()) } func TestRunTaskInSelectedCandidatePairChangeCallback(t *testing.T) { @@ -1521,7 +1516,7 @@ func TestRunTaskInSelectedCandidatePairChangeCallback(t *testing.T) { if err = aAgent.OnSelectedCandidatePairChange(func(Candidate, Candidate) { go func() { _, _, errCred := aAgent.GetLocalUserCredentials() - assert.NoError(t, errCred) + require.NoError(t, errCred) close(isTested) }() }); err != nil { @@ -1539,8 +1534,8 @@ func TestRunTaskInSelectedCandidatePairChangeCallback(t *testing.T) { <-isComplete <-isTested - assert.NoError(t, aAgent.Close()) - assert.NoError(t, bAgent.Close()) + require.NoError(t, aAgent.Close()) + require.NoError(t, bAgent.Close()) } // Assert that a Lite agent goes to disconnected and failed @@ -1596,27 +1591,27 @@ func TestLiteLifecycle(t *testing.T) { <-aConnected <-bConnected - assert.NoError(t, aAgent.Close()) + require.NoError(t, aAgent.Close()) <-bDisconnected <-bFailed - assert.NoError(t, bAgent.Close()) + require.NoError(t, bAgent.Close()) } func TestNilCandidate(t *testing.T) { a, err := NewAgent(&AgentConfig{}) - assert.NoError(t, err) + require.NoError(t, err) - assert.NoError(t, a.AddRemoteCandidate(nil)) - assert.NoError(t, a.Close()) + require.NoError(t, a.AddRemoteCandidate(nil)) + require.NoError(t, a.Close()) } func TestNilCandidatePair(t *testing.T) { a, err := NewAgent(&AgentConfig{}) - assert.NoError(t, err) + require.NoError(t, err) a.setSelectedPair(nil) - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) } func TestGetSelectedCandidatePair(t *testing.T) { @@ -1630,15 +1625,15 @@ func TestGetSelectedCandidatePair(t *testing.T) { CIDR: "0.0.0.0/0", LoggerFactory: logging.NewDefaultLoggerFactory(), }) - assert.NoError(t, err) + require.NoError(t, err) net, err := vnet.NewNet(&vnet.NetConfig{ StaticIPs: []string{"192.168.0.1"}, }) - assert.NoError(t, err) - assert.NoError(t, wan.AddNet(net)) + require.NoError(t, err) + require.NoError(t, wan.AddNet(net)) - assert.NoError(t, wan.Start()) + require.NoError(t, wan.Start()) cfg := &AgentConfig{ NetworkTypes: supportedNetworkTypes(), @@ -1646,35 +1641,35 @@ func TestGetSelectedCandidatePair(t *testing.T) { } aAgent, err := NewAgent(cfg) - assert.NoError(t, err) + require.NoError(t, err) bAgent, err := NewAgent(cfg) - assert.NoError(t, err) + require.NoError(t, err) aAgentPair, err := aAgent.GetSelectedCandidatePair() - assert.NoError(t, err) - assert.Nil(t, aAgentPair) + require.NoError(t, err) + require.Nil(t, aAgentPair) bAgentPair, err := bAgent.GetSelectedCandidatePair() - assert.NoError(t, err) - assert.Nil(t, bAgentPair) + require.NoError(t, err) + require.Nil(t, bAgentPair) connect(aAgent, bAgent) aAgentPair, err = aAgent.GetSelectedCandidatePair() - assert.NoError(t, err) - assert.NotNil(t, aAgentPair) + require.NoError(t, err) + require.NotNil(t, aAgentPair) bAgentPair, err = bAgent.GetSelectedCandidatePair() - assert.NoError(t, err) - assert.NotNil(t, bAgentPair) + require.NoError(t, err) + require.NotNil(t, bAgentPair) - assert.True(t, bAgentPair.Local.Equal(aAgentPair.Remote)) - assert.True(t, bAgentPair.Remote.Equal(aAgentPair.Local)) + require.True(t, bAgentPair.Local.Equal(aAgentPair.Remote)) + require.True(t, bAgentPair.Remote.Equal(aAgentPair.Local)) - assert.NoError(t, wan.Stop()) - assert.NoError(t, aAgent.Close()) - assert.NoError(t, bAgent.Close()) + require.NoError(t, wan.Stop()) + require.NoError(t, aAgent.Close()) + require.NoError(t, bAgent.Close()) } func TestAcceptAggressiveNomination(t *testing.T) { @@ -1689,21 +1684,21 @@ func TestAcceptAggressiveNomination(t *testing.T) { CIDR: "0.0.0.0/0", LoggerFactory: logging.NewDefaultLoggerFactory(), }) - assert.NoError(t, err) + require.NoError(t, err) net0, err := vnet.NewNet(&vnet.NetConfig{ StaticIPs: []string{"192.168.0.1"}, }) - assert.NoError(t, err) - assert.NoError(t, wan.AddNet(net0)) + require.NoError(t, err) + require.NoError(t, wan.AddNet(net0)) net1, err := vnet.NewNet(&vnet.NetConfig{ StaticIPs: []string{"192.168.0.2", "192.168.0.3", "192.168.0.4"}, }) - assert.NoError(t, err) - assert.NoError(t, wan.AddNet(net1)) + require.NoError(t, err) + require.NoError(t, wan.AddNet(net1)) - assert.NoError(t, wan.Start()) + require.NoError(t, wan.Start()) aNotifier, aConnected := onConnected() bNotifier, bConnected := onConnected() @@ -1791,13 +1786,11 @@ func TestAcceptAggressiveNomination(t *testing.T) { time.Sleep(1 * time.Second) select { case selected := <-selectedCh: - assert.True(t, selected.Equal(expectNewSelectedCandidate)) + require.True(t, selected.Equal(expectNewSelectedCandidate)) default: t.Fatal("No selected candidate pair") } - assert.NoError(t, wan.Stop()) - if !closePipe(t, aConn, bConn) { - return - } + require.NoError(t, wan.Stop()) + closePipe(t, aConn, bConn) } diff --git a/candidate_relay_test.go b/candidate_relay_test.go index 59b89ab..cf832e4 100644 --- a/candidate_relay_test.go +++ b/candidate_relay_test.go @@ -15,7 +15,7 @@ import ( "github.com/pion/stun/v2" "github.com/pion/transport/v3/test" "github.com/pion/turn/v3" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func optimisticAuthHandler(string, string, net.Addr) (key []byte, ok bool) { @@ -32,7 +32,7 @@ func TestRelayOnlyConnection(t *testing.T) { serverPort := randomPort(t) serverListener, err := net.ListenPacket("udp", localhostIPStr+":"+strconv.Itoa(serverPort)) - assert.NoError(t, err) + require.NoError(t, err) server, err := turn.NewServer(turn.ServerConfig{ Realm: "pion.ly", @@ -44,7 +44,7 @@ func TestRelayOnlyConnection(t *testing.T) { }, }, }) - assert.NoError(t, err) + require.NoError(t, err) cfg := &AgentConfig{ NetworkTypes: supportedNetworkTypes(), @@ -85,7 +85,7 @@ func TestRelayOnlyConnection(t *testing.T) { <-aConnected <-bConnected - assert.NoError(t, aAgent.Close()) - assert.NoError(t, bAgent.Close()) - assert.NoError(t, server.Close()) + require.NoError(t, aAgent.Close()) + require.NoError(t, bAgent.Close()) + require.NoError(t, server.Close()) } diff --git a/candidate_server_reflexive_test.go b/candidate_server_reflexive_test.go index 236ab03..5f2b162 100644 --- a/candidate_server_reflexive_test.go +++ b/candidate_server_reflexive_test.go @@ -15,7 +15,7 @@ import ( "github.com/pion/stun/v2" "github.com/pion/transport/v3/test" "github.com/pion/turn/v3" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestServerReflexiveOnlyConnection(t *testing.T) { @@ -28,7 +28,7 @@ func TestServerReflexiveOnlyConnection(t *testing.T) { serverPort := randomPort(t) serverListener, err := net.ListenPacket("udp4", "127.0.0.1:"+strconv.Itoa(serverPort)) - assert.NoError(t, err) + require.NoError(t, err) server, err := turn.NewServer(turn.ServerConfig{ Realm: "pion.ly", @@ -40,7 +40,7 @@ func TestServerReflexiveOnlyConnection(t *testing.T) { }, }, }) - assert.NoError(t, err) + require.NoError(t, err) cfg := &AgentConfig{ NetworkTypes: []NetworkType{NetworkTypeUDP4}, @@ -78,7 +78,7 @@ func TestServerReflexiveOnlyConnection(t *testing.T) { <-aConnected <-bConnected - assert.NoError(t, aAgent.Close()) - assert.NoError(t, bAgent.Close()) - assert.NoError(t, server.Close()) + require.NoError(t, aAgent.Close()) + require.NoError(t, bAgent.Close()) + require.NoError(t, server.Close()) } diff --git a/candidate_test.go b/candidate_test.go index 5078154..96ab87c 100644 --- a/candidate_test.go +++ b/candidate_test.go @@ -9,7 +9,6 @@ import ( "time" "github.com/pion/logging" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -183,23 +182,23 @@ func TestCandidatePriority(t *testing.T) { func TestCandidateLastSent(t *testing.T) { candidate := candidateBase{} - assert.Equal(t, candidate.LastSent(), time.Time{}) + require.Equal(t, candidate.LastSent(), time.Time{}) now := time.Now() candidate.setLastSent(now) - assert.Equal(t, candidate.LastSent(), now) + require.Equal(t, candidate.LastSent(), now) } func TestCandidateLastReceived(t *testing.T) { candidate := candidateBase{} - assert.Equal(t, candidate.LastReceived(), time.Time{}) + require.Equal(t, candidate.LastReceived(), time.Time{}) now := time.Now() candidate.setLastReceived(now) - assert.Equal(t, candidate.LastReceived(), now) + require.Equal(t, candidate.LastReceived(), now) } func TestCandidateFoundation(t *testing.T) { // All fields are the same - assert.Equal(t, + require.Equal(t, (&candidateBase{ candidateType: CandidateTypeHost, networkType: NetworkTypeUDP4, @@ -212,7 +211,7 @@ func TestCandidateFoundation(t *testing.T) { }).Foundation()) // Different Address - assert.NotEqual(t, + require.NotEqual(t, (&candidateBase{ candidateType: CandidateTypeHost, networkType: NetworkTypeUDP4, @@ -225,7 +224,7 @@ func TestCandidateFoundation(t *testing.T) { }).Foundation()) // Different networkType - assert.NotEqual(t, + require.NotEqual(t, (&candidateBase{ candidateType: CandidateTypeHost, networkType: NetworkTypeUDP4, @@ -238,7 +237,7 @@ func TestCandidateFoundation(t *testing.T) { }).Foundation()) // Different candidateType - assert.NotEqual(t, + require.NotEqual(t, (&candidateBase{ candidateType: CandidateTypeHost, networkType: NetworkTypeUDP4, @@ -251,7 +250,7 @@ func TestCandidateFoundation(t *testing.T) { }).Foundation()) // Port has no effect - assert.Equal(t, + require.Equal(t, (&candidateBase{ candidateType: CandidateTypeHost, networkType: NetworkTypeUDP4, @@ -387,14 +386,14 @@ func TestCandidateMarshal(t *testing.T) { } { actualCandidate, err := UnmarshalCandidate(test.marshaled) if test.expectError { - assert.Error(t, err) + require.Error(t, err) continue } - assert.NoError(t, err) + require.NoError(t, err) - assert.True(t, test.candidate.Equal(actualCandidate)) - assert.Equal(t, test.marshaled, actualCandidate.Marshal()) + require.True(t, test.candidate.Equal(actualCandidate)) + require.Equal(t, test.marshaled, actualCandidate.Marshal()) } } @@ -429,11 +428,11 @@ func TestCandidateWriteTo(t *testing.T) { } _, err = c1.writeTo([]byte("test"), c2) - assert.NoError(t, err, "writing to open conn") + require.NoError(t, err, "writing to open conn") err = packetConn.Close() require.NoError(t, err, "error closing test TCP connection") _, err = c1.writeTo([]byte("test"), c2) - assert.Error(t, err, "writing to closed conn") + require.Error(t, err, "writing to closed conn") } diff --git a/candidatepair_test.go b/candidatepair_test.go index 58653bd..a338a69 100644 --- a/candidatepair_test.go +++ b/candidatepair_test.go @@ -6,7 +6,7 @@ package ice import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func hostCandidate() *CandidateHost { @@ -132,5 +132,5 @@ func TestCandidatePairEquality(t *testing.T) { func TestNilCandidatePairString(t *testing.T) { var nilCandidatePair *CandidatePair - assert.Equal(t, nilCandidatePair.String(), "") + require.Equal(t, nilCandidatePair.String(), "") } diff --git a/connectivity_vnet_test.go b/connectivity_vnet_test.go index 0aadc95..99bafa5 100644 --- a/connectivity_vnet_test.go +++ b/connectivity_vnet_test.go @@ -19,7 +19,7 @@ import ( "github.com/pion/transport/v3/test" "github.com/pion/transport/v3/vnet" "github.com/pion/turn/v3" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const ( @@ -292,13 +292,9 @@ func pipeWithVNet(v *virtualNet, a0TestConfig, a1TestConfig *agentTestConfig) (* return aConn, bConn } -func closePipe(t *testing.T, ca *Conn, cb *Conn) bool { - err := ca.Close() - if !assert.NoError(t, err, "should succeed") { - return false - } - err = cb.Close() - return assert.NoError(t, err, "should succeed") +func closePipe(t *testing.T, ca *Conn, cb *Conn) { + require.NoError(t, ca.Close()) + require.NoError(t, cb.Close()) } func TestConnectivityVNet(t *testing.T) { @@ -332,9 +328,7 @@ func TestConnectivityVNet(t *testing.T) { } v, err := buildVNet(natType, natType) - if !assert.NoError(t, err, "should succeed") { - return - } + require.NoError(t, err, "should succeed") defer v.close() log.Debug("Connecting...") @@ -353,9 +347,7 @@ func TestConnectivityVNet(t *testing.T) { time.Sleep(1 * time.Second) log.Debug("Closing...") - if !closePipe(t, ca, cb) { - return - } + closePipe(t, ca, cb) }) t.Run("Symmetric NATs on both ends", func(t *testing.T) { @@ -369,9 +361,7 @@ func TestConnectivityVNet(t *testing.T) { } v, err := buildVNet(natType, natType) - if !assert.NoError(t, err, "should succeed") { - return - } + require.NoError(t, err, "should succeed") defer v.close() log.Debug("Connecting...") @@ -389,9 +379,7 @@ func TestConnectivityVNet(t *testing.T) { ca, cb := pipeWithVNet(v, a0TestConfig, a1TestConfig) log.Debug("Closing...") - if !closePipe(t, ca, cb) { - return - } + closePipe(t, ca, cb) }) t.Run("1:1 NAT with host candidate vs Symmetric NATs", func(t *testing.T) { @@ -409,9 +397,7 @@ func TestConnectivityVNet(t *testing.T) { } v, err := buildVNet(natType0, natType1) - if !assert.NoError(t, err, "should succeed") { - return - } + require.NoError(t, err, "should succeed") defer v.close() log.Debug("Connecting...") @@ -425,9 +411,7 @@ func TestConnectivityVNet(t *testing.T) { ca, cb := pipeWithVNet(v, a0TestConfig, a1TestConfig) log.Debug("Closing...") - if !closePipe(t, ca, cb) { - return - } + closePipe(t, ca, cb) }) t.Run("1:1 NAT with srflx candidate vs Symmetric NATs", func(t *testing.T) { @@ -445,9 +429,7 @@ func TestConnectivityVNet(t *testing.T) { } v, err := buildVNet(natType0, natType1) - if !assert.NoError(t, err, "should succeed") { - return - } + require.NoError(t, err, "should succeed") defer v.close() log.Debug("Connecting...") @@ -461,13 +443,11 @@ func TestConnectivityVNet(t *testing.T) { ca, cb := pipeWithVNet(v, a0TestConfig, a1TestConfig) log.Debug("Closing...") - if !closePipe(t, ca, cb) { - return - } + closePipe(t, ca, cb) }) } -// TestDisconnectedToConnected asserts that an agent can go to disconnected, and then return to connected successfully +// TestDisconnectedToConnected requires that an agent can go to disconnected, and then return to connected successfully func TestDisconnectedToConnected(t *testing.T) { report := test.CheckRoutines(t) defer report() @@ -482,7 +462,7 @@ func TestDisconnectedToConnected(t *testing.T) { CIDR: "0.0.0.0/0", LoggerFactory: loggerFactory, }) - assert.NoError(t, err) + require.NoError(t, err) var dropAllData uint64 wan.AddChunkFilter(func(vnet.Chunk) bool { @@ -492,16 +472,16 @@ func TestDisconnectedToConnected(t *testing.T) { net0, err := vnet.NewNet(&vnet.NetConfig{ StaticIPs: []string{"192.168.0.1"}, }) - assert.NoError(t, err) - assert.NoError(t, wan.AddNet(net0)) + require.NoError(t, err) + require.NoError(t, wan.AddNet(net0)) net1, err := vnet.NewNet(&vnet.NetConfig{ StaticIPs: []string{"192.168.0.2"}, }) - assert.NoError(t, err) - assert.NoError(t, wan.AddNet(net1)) + require.NoError(t, err) + require.NoError(t, wan.AddNet(net1)) - assert.NoError(t, wan.Start()) + require.NoError(t, wan.Start()) disconnectTimeout := time.Second keepaliveInterval := time.Millisecond * 20 @@ -515,7 +495,7 @@ func TestDisconnectedToConnected(t *testing.T) { KeepaliveInterval: &keepaliveInterval, CheckInterval: &keepaliveInterval, }) - assert.NoError(t, err) + require.NoError(t, err) controlledAgent, err := NewAgent(&AgentConfig{ NetworkTypes: supportedNetworkTypes(), @@ -525,15 +505,15 @@ func TestDisconnectedToConnected(t *testing.T) { KeepaliveInterval: &keepaliveInterval, CheckInterval: &keepaliveInterval, }) - assert.NoError(t, err) + require.NoError(t, err) controllingStateChanges := make(chan ConnectionState, 100) - assert.NoError(t, controllingAgent.OnConnectionStateChange(func(c ConnectionState) { + require.NoError(t, controllingAgent.OnConnectionStateChange(func(c ConnectionState) { controllingStateChanges <- c })) controlledStateChanges := make(chan ConnectionState, 100) - assert.NoError(t, controlledAgent.OnConnectionStateChange(func(c ConnectionState) { + require.NoError(t, controlledAgent.OnConnectionStateChange(func(c ConnectionState) { controlledStateChanges <- c })) @@ -560,9 +540,9 @@ func TestDisconnectedToConnected(t *testing.T) { blockUntilStateSeen(ConnectionStateConnected, controllingStateChanges) blockUntilStateSeen(ConnectionStateConnected, controlledStateChanges) - assert.NoError(t, wan.Stop()) - assert.NoError(t, controllingAgent.Close()) - assert.NoError(t, controlledAgent.Close()) + require.NoError(t, wan.Stop()) + require.NoError(t, controllingAgent.Close()) + require.NoError(t, controlledAgent.Close()) } // Agent.Write should use the best valid pair if a selected pair is not yet available @@ -580,7 +560,7 @@ func TestWriteUseValidPair(t *testing.T) { CIDR: "0.0.0.0/0", LoggerFactory: loggerFactory, }) - assert.NoError(t, err) + require.NoError(t, err) wan.AddChunkFilter(func(c vnet.Chunk) bool { if stun.IsMessage(c.UserData()) { @@ -600,16 +580,16 @@ func TestWriteUseValidPair(t *testing.T) { net0, err := vnet.NewNet(&vnet.NetConfig{ StaticIPs: []string{"192.168.0.1"}, }) - assert.NoError(t, err) - assert.NoError(t, wan.AddNet(net0)) + require.NoError(t, err) + require.NoError(t, wan.AddNet(net0)) net1, err := vnet.NewNet(&vnet.NetConfig{ StaticIPs: []string{"192.168.0.2"}, }) - assert.NoError(t, err) - assert.NoError(t, wan.AddNet(net1)) + require.NoError(t, err) + require.NoError(t, wan.AddNet(net1)) - assert.NoError(t, wan.Start()) + require.NoError(t, wan.Start()) // Create two agents and connect them controllingAgent, err := NewAgent(&AgentConfig{ @@ -617,25 +597,25 @@ func TestWriteUseValidPair(t *testing.T) { MulticastDNSMode: MulticastDNSModeDisabled, Net: net0, }) - assert.NoError(t, err) + require.NoError(t, err) controlledAgent, err := NewAgent(&AgentConfig{ NetworkTypes: supportedNetworkTypes(), MulticastDNSMode: MulticastDNSModeDisabled, Net: net1, }) - assert.NoError(t, err) + require.NoError(t, err) gatherAndExchangeCandidates(controllingAgent, controlledAgent) controllingUfrag, controllingPwd, err := controllingAgent.GetLocalUserCredentials() - assert.NoError(t, err) + require.NoError(t, err) controlledUfrag, controlledPwd, err := controlledAgent.GetLocalUserCredentials() - assert.NoError(t, err) + require.NoError(t, err) - assert.NoError(t, controllingAgent.startConnectivityChecks(true, controlledUfrag, controlledPwd)) - assert.NoError(t, controlledAgent.startConnectivityChecks(false, controllingUfrag, controllingPwd)) + require.NoError(t, controllingAgent.startConnectivityChecks(true, controlledUfrag, controlledPwd)) + require.NoError(t, controlledAgent.startConnectivityChecks(false, controllingUfrag, controllingPwd)) testMessage := []byte("Test Message") go func() { @@ -650,11 +630,11 @@ func TestWriteUseValidPair(t *testing.T) { readBuf := make([]byte, len(testMessage)) _, err = (&Conn{agent: controlledAgent}).Read(readBuf) - assert.NoError(t, err) + require.NoError(t, err) - assert.Equal(t, readBuf, testMessage) + require.Equal(t, readBuf, testMessage) - assert.NoError(t, wan.Stop()) - assert.NoError(t, controllingAgent.Close()) - assert.NoError(t, controlledAgent.Close()) + require.NoError(t, wan.Stop()) + require.NoError(t, controllingAgent.Close()) + require.NoError(t, controlledAgent.Close()) } diff --git a/external_ip_mapper_test.go b/external_ip_mapper_test.go index dbe39ad..76b3d8d 100644 --- a/external_ip_mapper_test.go +++ b/external_ip_mapper_test.go @@ -7,7 +7,7 @@ import ( "net" "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestExternalIPMapper(t *testing.T) { @@ -17,17 +17,17 @@ func TestExternalIPMapper(t *testing.T) { var err error ip, isIPv4, err = validateIPString("1.2.3.4") - assert.NoError(t, err, "should succeed") - assert.True(t, isIPv4, "should be true") - assert.Equal(t, "1.2.3.4", ip.String(), "should be true") + require.NoError(t, err, "should succeed") + require.True(t, isIPv4, "should be true") + require.Equal(t, "1.2.3.4", ip.String(), "should be true") ip, isIPv4, err = validateIPString("2601:4567::5678") - assert.NoError(t, err, "should succeed") - assert.False(t, isIPv4, "should be false") - assert.Equal(t, "2601:4567::5678", ip.String(), "should be true") + require.NoError(t, err, "should succeed") + require.False(t, isIPv4, "should be false") + require.Equal(t, "2601:4567::5678", ip.String(), "should be true") _, _, err = validateIPString("bad.6.6.6") - assert.Error(t, err, "should fail") + require.Error(t, err, "should fail") }) t.Run("newExternalIPMapper", func(t *testing.T) { @@ -36,106 +36,106 @@ func TestExternalIPMapper(t *testing.T) { // ips being nil should succeed but mapper will be nil also m, err = newExternalIPMapper(CandidateTypeUnspecified, nil) - assert.NoError(t, err, "should succeed") - assert.Nil(t, m, "should be nil") + require.NoError(t, err, "should succeed") + require.Nil(t, m, "should be nil") // ips being empty should succeed but mapper will still be nil m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{}) - assert.NoError(t, err, "should succeed") - assert.Nil(t, m, "should be nil") + require.NoError(t, err, "should succeed") + require.Nil(t, m, "should be nil") // IPv4 with no explicit local IP, defaults to CandidateTypeHost m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.4", }) - assert.NoError(t, err, "should succeed") - assert.NotNil(t, m, "should not be nil") - assert.Equal(t, CandidateTypeHost, m.candidateType, "should match") - assert.NotNil(t, m.ipv4Mapping.ipSole) - assert.Nil(t, m.ipv6Mapping.ipSole) - assert.Equal(t, 0, len(m.ipv4Mapping.ipMap), "should match") - assert.Equal(t, 0, len(m.ipv6Mapping.ipMap), "should match") + require.NoError(t, err, "should succeed") + require.NotNil(t, m, "should not be nil") + require.Equal(t, CandidateTypeHost, m.candidateType, "should match") + require.NotNil(t, m.ipv4Mapping.ipSole) + require.Nil(t, m.ipv6Mapping.ipSole) + require.Equal(t, 0, len(m.ipv4Mapping.ipMap), "should match") + require.Equal(t, 0, len(m.ipv6Mapping.ipMap), "should match") // IPv4 with no explicit local IP, using CandidateTypeServerReflexive m, err = newExternalIPMapper(CandidateTypeServerReflexive, []string{ "1.2.3.4", }) - assert.NoError(t, err, "should succeed") - assert.NotNil(t, m, "should not be nil") - assert.Equal(t, CandidateTypeServerReflexive, m.candidateType, "should match") - assert.NotNil(t, m.ipv4Mapping.ipSole) - assert.Nil(t, m.ipv6Mapping.ipSole) - assert.Equal(t, 0, len(m.ipv4Mapping.ipMap), "should match") - assert.Equal(t, 0, len(m.ipv6Mapping.ipMap), "should match") + require.NoError(t, err, "should succeed") + require.NotNil(t, m, "should not be nil") + require.Equal(t, CandidateTypeServerReflexive, m.candidateType, "should match") + require.NotNil(t, m.ipv4Mapping.ipSole) + require.Nil(t, m.ipv6Mapping.ipSole) + require.Equal(t, 0, len(m.ipv4Mapping.ipMap), "should match") + require.Equal(t, 0, len(m.ipv6Mapping.ipMap), "should match") // IPv4 with no explicit local IP, defaults to CandidateTypeHost m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "2601:4567::5678", }) - assert.NoError(t, err, "should succeed") - assert.NotNil(t, m, "should not be nil") - assert.Equal(t, CandidateTypeHost, m.candidateType, "should match") - assert.Nil(t, m.ipv4Mapping.ipSole) - assert.NotNil(t, m.ipv6Mapping.ipSole) - assert.Equal(t, 0, len(m.ipv4Mapping.ipMap), "should match") - assert.Equal(t, 0, len(m.ipv6Mapping.ipMap), "should match") + require.NoError(t, err, "should succeed") + require.NotNil(t, m, "should not be nil") + require.Equal(t, CandidateTypeHost, m.candidateType, "should match") + require.Nil(t, m.ipv4Mapping.ipSole) + require.NotNil(t, m.ipv6Mapping.ipSole) + require.Equal(t, 0, len(m.ipv4Mapping.ipMap), "should match") + require.Equal(t, 0, len(m.ipv6Mapping.ipMap), "should match") // IPv4 and IPv6 in the mix m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.4", "2601:4567::5678", }) - assert.NoError(t, err, "should succeed") - assert.NotNil(t, m, "should not be nil") - assert.Equal(t, CandidateTypeHost, m.candidateType, "should match") - assert.NotNil(t, m.ipv4Mapping.ipSole) - assert.NotNil(t, m.ipv6Mapping.ipSole) - assert.Equal(t, 0, len(m.ipv4Mapping.ipMap), "should match") - assert.Equal(t, 0, len(m.ipv6Mapping.ipMap), "should match") + require.NoError(t, err, "should succeed") + require.NotNil(t, m, "should not be nil") + require.Equal(t, CandidateTypeHost, m.candidateType, "should match") + require.NotNil(t, m.ipv4Mapping.ipSole) + require.NotNil(t, m.ipv6Mapping.ipSole) + require.Equal(t, 0, len(m.ipv4Mapping.ipMap), "should match") + require.Equal(t, 0, len(m.ipv6Mapping.ipMap), "should match") // Unsupported candidate type - CandidateTypePeerReflexive m, err = newExternalIPMapper(CandidateTypePeerReflexive, []string{ "1.2.3.4", }) - assert.Error(t, err, "should fail") - assert.Nil(t, m, "should be nil") + require.Error(t, err, "should fail") + require.Nil(t, m, "should be nil") // Unsupported candidate type - CandidateTypeRelay m, err = newExternalIPMapper(CandidateTypePeerReflexive, []string{ "1.2.3.4", }) - assert.Error(t, err, "should fail") - assert.Nil(t, m, "should be nil") + require.Error(t, err, "should fail") + require.Nil(t, m, "should be nil") // Cannot duplicate mapping IPv4 family m, err = newExternalIPMapper(CandidateTypeServerReflexive, []string{ "1.2.3.4", "5.6.7.8", }) - assert.Error(t, err, "should fail") - assert.Nil(t, m, "should be nil") + require.Error(t, err, "should fail") + require.Nil(t, m, "should be nil") // Cannot duplicate mapping IPv6 family m, err = newExternalIPMapper(CandidateTypeServerReflexive, []string{ "2201::1", "2201::0002", }) - assert.Error(t, err, "should fail") - assert.Nil(t, m, "should be nil") + require.Error(t, err, "should fail") + require.Nil(t, m, "should be nil") // Invalide external IP string m, err = newExternalIPMapper(CandidateTypeServerReflexive, []string{ "bad.2.3.4", }) - assert.Error(t, err, "should fail") - assert.Nil(t, m, "should be nil") + require.Error(t, err, "should fail") + require.Nil(t, m, "should be nil") // Invalide local IP string m, err = newExternalIPMapper(CandidateTypeServerReflexive, []string{ "1.2.3.4/10.0.0.bad", }) - assert.Error(t, err, "should fail") - assert.Nil(t, m, "should be nil") + require.Error(t, err, "should fail") + require.Nil(t, m, "should be nil") }) t.Run("newExternalIPMapper with explicit local IP", func(t *testing.T) { @@ -146,50 +146,50 @@ func TestExternalIPMapper(t *testing.T) { m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.4/10.0.0.1", }) - assert.NoError(t, err, "should succeed") - assert.NotNil(t, m, "should not be nil") - assert.Equal(t, CandidateTypeHost, m.candidateType, "should match") - assert.Nil(t, m.ipv4Mapping.ipSole) - assert.Nil(t, m.ipv6Mapping.ipSole) - assert.Equal(t, 1, len(m.ipv4Mapping.ipMap), "should match") - assert.Equal(t, 0, len(m.ipv6Mapping.ipMap), "should match") + require.NoError(t, err, "should succeed") + require.NotNil(t, m, "should not be nil") + require.Equal(t, CandidateTypeHost, m.candidateType, "should match") + require.Nil(t, m.ipv4Mapping.ipSole) + require.Nil(t, m.ipv6Mapping.ipSole) + require.Equal(t, 1, len(m.ipv4Mapping.ipMap), "should match") + require.Equal(t, 0, len(m.ipv6Mapping.ipMap), "should match") // Cannot assign two ext IPs for one local IPv4 m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.4/10.0.0.1", "1.2.3.5/10.0.0.1", }) - assert.Error(t, err, "should fail") - assert.Nil(t, m, "should be nil") + require.Error(t, err, "should fail") + require.Nil(t, m, "should be nil") // Cannot assign two ext IPs for one local IPv6 m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "2200::1/fe80::1", "2200::0002/fe80::1", }) - assert.Error(t, err, "should fail") - assert.Nil(t, m, "should be nil") + require.Error(t, err, "should fail") + require.Nil(t, m, "should be nil") // Cannot mix different IP family in a pair (1) m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "2200::1/10.0.0.1", }) - assert.Error(t, err, "should fail") - assert.Nil(t, m, "should be nil") + require.Error(t, err, "should fail") + require.Nil(t, m, "should be nil") // Cannot mix different IP family in a pair (2) m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.4/fe80::1", }) - assert.Error(t, err, "should fail") - assert.Nil(t, m, "should be nil") + require.Error(t, err, "should fail") + require.Nil(t, m, "should be nil") // Invalid pair m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.4/192.168.0.2/10.0.0.1", }) - assert.Error(t, err, "should fail") - assert.Nil(t, m, "should be nil") + require.Error(t, err, "should fail") + require.Nil(t, m, "should be nil") }) t.Run("newExternalIPMapper with implicit and explicit local IP", func(t *testing.T) { @@ -198,14 +198,14 @@ func TestExternalIPMapper(t *testing.T) { "1.2.3.4", "1.2.3.5/10.0.0.1", }) - assert.Error(t, err, "should fail") + require.Error(t, err, "should fail") // Mixing implicit and explicit local IPs not allowed _, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.5/10.0.0.1", "1.2.3.4", }) - assert.Error(t, err, "should fail") + require.Error(t, err, "should fail") }) t.Run("findExternalIP without explicit local IP", func(t *testing.T) { @@ -218,24 +218,24 @@ func TestExternalIPMapper(t *testing.T) { "1.2.3.4", "2200::1", }) - assert.NoError(t, err, "should succeed") - assert.NotNil(t, m, "should not be nil") - assert.NotNil(t, m.ipv4Mapping.ipSole) - assert.NotNil(t, m.ipv6Mapping.ipSole) + require.NoError(t, err, "should succeed") + require.NotNil(t, m, "should not be nil") + require.NotNil(t, m.ipv4Mapping.ipSole) + require.NotNil(t, m.ipv6Mapping.ipSole) // Find external IPv4 extIP, err = m.findExternalIP("10.0.0.1") - assert.NoError(t, err, "should succeed") - assert.Equal(t, "1.2.3.4", extIP.String(), "should match") + require.NoError(t, err, "should succeed") + require.Equal(t, "1.2.3.4", extIP.String(), "should match") // Find external IPv6 extIP, err = m.findExternalIP("fe80::0001") // Use '0001' instead of '1' on purpose - assert.NoError(t, err, "should succeed") - assert.Equal(t, "2200::1", extIP.String(), "should match") + require.NoError(t, err, "should succeed") + require.Equal(t, "2200::1", extIP.String(), "should match") // Bad local IP string _, err = m.findExternalIP("really.bad") - assert.Error(t, err, "should fail") + require.Error(t, err, "should fail") }) t.Run("findExternalIP with explicit local IP", func(t *testing.T) { @@ -250,36 +250,36 @@ func TestExternalIPMapper(t *testing.T) { "2200::1/fe80::1", "2200::2/fe80::2", }) - assert.NoError(t, err, "should succeed") - assert.NotNil(t, m, "should not be nil") + require.NoError(t, err, "should succeed") + require.NotNil(t, m, "should not be nil") // Find external IPv4 extIP, err = m.findExternalIP("10.0.0.1") - assert.NoError(t, err, "should succeed") - assert.Equal(t, "1.2.3.4", extIP.String(), "should match") + require.NoError(t, err, "should succeed") + require.Equal(t, "1.2.3.4", extIP.String(), "should match") extIP, err = m.findExternalIP("10.0.0.2") - assert.NoError(t, err, "should succeed") - assert.Equal(t, "1.2.3.5", extIP.String(), "should match") + require.NoError(t, err, "should succeed") + require.Equal(t, "1.2.3.5", extIP.String(), "should match") _, err = m.findExternalIP("10.0.0.3") - assert.Error(t, err, "should fail") + require.Error(t, err, "should fail") // Find external IPv6 extIP, err = m.findExternalIP("fe80::0001") // Use '0001' instead of '1' on purpose - assert.NoError(t, err, "should succeed") - assert.Equal(t, "2200::1", extIP.String(), "should match") + require.NoError(t, err, "should succeed") + require.Equal(t, "2200::1", extIP.String(), "should match") extIP, err = m.findExternalIP("fe80::0002") // Use '0002' instead of '2' on purpose - assert.NoError(t, err, "should succeed") - assert.Equal(t, "2200::2", extIP.String(), "should match") + require.NoError(t, err, "should succeed") + require.Equal(t, "2200::2", extIP.String(), "should match") _, err = m.findExternalIP("fe80::3") - assert.Error(t, err, "should fail") + require.Error(t, err, "should fail") // Bad local IP string _, err = m.findExternalIP("really.bad") - assert.Error(t, err, "should fail") + require.Error(t, err, "should fail") }) t.Run("findExternalIP with empty map", func(t *testing.T) { @@ -289,21 +289,21 @@ func TestExternalIPMapper(t *testing.T) { m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.4", }) - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") // Attempt to find IPv6 that does not exist in the map extIP, err := m.findExternalIP("fe80::1") - assert.NoError(t, err, "should succeed") - assert.Equal(t, "fe80::1", extIP.String(), "should match") + require.NoError(t, err, "should succeed") + require.Equal(t, "fe80::1", extIP.String(), "should match") m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "2200::1", }) - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") // Attempt to find IPv4 that does not exist in the map extIP, err = m.findExternalIP("10.0.0.1") - assert.NoError(t, err, "should succeed") - assert.Equal(t, "10.0.0.1", extIP.String(), "should match") + require.NoError(t, err, "should succeed") + require.Equal(t, "10.0.0.1", extIP.String(), "should match") }) } diff --git a/gather_test.go b/gather_test.go index 8f31cd6..e070bb8 100644 --- a/gather_test.go +++ b/gather_test.go @@ -26,35 +26,34 @@ import ( "github.com/pion/stun/v2" "github.com/pion/transport/v3/test" "github.com/pion/turn/v3" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/net/proxy" ) func TestListenUDP(t *testing.T) { a, err := NewAgent(&AgentConfig{}) - assert.NoError(t, err) + require.NoError(t, err) localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) - assert.NotEqual(t, len(localIPs), 0, "localInterfaces found no interfaces, unable to test") - assert.NoError(t, err) + require.NotEqual(t, len(localIPs), 0, "localInterfaces found no interfaces, unable to test") + require.NoError(t, err) ip := localIPs[0] conn, err := listenUDPInPortRange(a.net, a.log, 0, 0, udp, &net.UDPAddr{IP: ip, Port: 0}) - assert.NoError(t, err, "listenUDP error with no port restriction") - assert.NotNil(t, conn, "listenUDP error with no port restriction return a nil conn") + require.NoError(t, err, "listenUDP error with no port restriction") + require.NotNil(t, conn, "listenUDP error with no port restriction return a nil conn") _, err = listenUDPInPortRange(a.net, a.log, 4999, 5000, udp, &net.UDPAddr{IP: ip, Port: 0}) - assert.Equal(t, err, ErrPort, "listenUDP with invalid port range did not return ErrPort") + require.Equal(t, err, ErrPort, "listenUDP with invalid port range did not return ErrPort") conn, err = listenUDPInPortRange(a.net, a.log, 5000, 5000, udp, &net.UDPAddr{IP: ip, Port: 0}) - assert.NoError(t, err, "listenUDP error with no port restriction") - assert.NotNil(t, conn, "listenUDP error with no port restriction return a nil conn") + require.NoError(t, err, "listenUDP error with no port restriction") + require.NotNil(t, conn, "listenUDP error with no port restriction return a nil conn") _, port, err := net.SplitHostPort(conn.LocalAddr().String()) - assert.NoError(t, err) - assert.Equal(t, port, "5000", "listenUDP with port restriction of 5000 listened on incorrect port") + require.NoError(t, err) + require.Equal(t, port, "5000", "listenUDP with port restriction of 5000 listened on incorrect port") portMin := 5100 portMax := 5109 @@ -63,8 +62,8 @@ func TestListenUDP(t *testing.T) { portRange := make([]int, 0, total) for i := 0; i < total; i++ { conn, err = listenUDPInPortRange(a.net, a.log, portMax, portMin, udp, &net.UDPAddr{IP: ip, Port: 0}) - assert.NoError(t, err, "listenUDP error with no port restriction") - assert.NotNil(t, conn, "listenUDP error with no port restriction return a nil conn") + require.NoError(t, err, "listenUDP error with no port restriction") + require.NotNil(t, conn, "listenUDP error with no port restriction return a nil conn") _, port, err = net.SplitHostPort(conn.LocalAddr().String()) if err != nil { @@ -85,9 +84,9 @@ func TestListenUDP(t *testing.T) { t.Fatalf("listenUDP with port restriction [%d, %d], got:%v, want:%v", portMin, portMax, result, portRange) } _, err = listenUDPInPortRange(a.net, a.log, portMax, portMin, udp, &net.UDPAddr{IP: ip, Port: 0}) - assert.Equal(t, err, ErrPort, "listenUDP with port restriction [%d, %d], did not return ErrPort", portMin, portMax) + require.Equal(t, err, ErrPort, "listenUDP with port restriction [%d, %d], did not return ErrPort", portMin, portMax) - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) } func TestGatherConcurrency(t *testing.T) { @@ -101,10 +100,10 @@ func TestGatherConcurrency(t *testing.T) { NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}, IncludeLoopback: true, }) - assert.NoError(t, err) + require.NoError(t, err) candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) - assert.NoError(t, a.OnCandidate(func(Candidate) { + require.NoError(t, a.OnCandidate(func(Candidate) { candidateGatheredFunc() })) @@ -115,7 +114,7 @@ func TestGatherConcurrency(t *testing.T) { <-candidateGathered.Done() - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) } func TestLoopbackCandidate(t *testing.T) { @@ -130,12 +129,12 @@ func TestLoopbackCandidate(t *testing.T) { loExpected bool } mux, err := NewMultiUDPMuxFromPort(12500) - assert.NoError(t, err) + require.NoError(t, err) muxWithLo, errlo := NewMultiUDPMuxFromPort(12501, UDPMuxFromPortWithLoopback()) - assert.NoError(t, errlo) + require.NoError(t, errlo) unspecConn, errconn := net.ListenPacket("udp", ":0") - assert.NoError(t, errconn) + require.NoError(t, errconn) defer func() { _ = unspecConn.Close() }() @@ -199,11 +198,11 @@ func TestLoopbackCandidate(t *testing.T) { tcase := tc t.Run(tcase.name, func(t *testing.T) { a, err := NewAgent(tc.agentConfig) - assert.NoError(t, err) + require.NoError(t, err) candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) var loopback int32 - assert.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, a.OnCandidate(func(c Candidate) { if c != nil { if net.ParseIP(c.Address()).IsLoopback() { atomic.StoreInt32(&loopback, 1) @@ -214,18 +213,18 @@ func TestLoopbackCandidate(t *testing.T) { } t.Log(c.NetworkType(), c.Priority(), c) })) - assert.NoError(t, a.GatherCandidates()) + require.NoError(t, a.GatherCandidates()) <-candidateGathered.Done() - assert.NoError(t, a.Close()) - assert.Equal(t, tcase.loExpected, atomic.LoadInt32(&loopback) == 1) + require.NoError(t, a.Close()) + require.Equal(t, tcase.loExpected, atomic.LoadInt32(&loopback) == 1) }) } - assert.NoError(t, mux.Close()) - assert.NoError(t, muxWithLo.Close()) - assert.NoError(t, muxUnspecDefault.Close()) + require.NoError(t, mux.Close()) + require.NoError(t, muxWithLo.Close()) + require.NoError(t, muxUnspecDefault.Close()) } // Assert that STUN gathering is done concurrently @@ -238,7 +237,7 @@ func TestSTUNConcurrency(t *testing.T) { serverPort := randomPort(t) serverListener, err := net.ListenPacket("udp4", localhostIPStr+":"+strconv.Itoa(serverPort)) - assert.NoError(t, err) + require.NoError(t, err) server, err := turn.NewServer(turn.ServerConfig{ Realm: "pion.ly", @@ -250,7 +249,7 @@ func TestSTUNConcurrency(t *testing.T) { }, }, }) - assert.NoError(t, err) + require.NoError(t, err) urls := []*stun.URI{} for i := 0; i <= 10; i++ { @@ -286,22 +285,22 @@ func TestSTUNConcurrency(t *testing.T) { }, ), }) - assert.NoError(t, err) + require.NoError(t, err) candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) - assert.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, a.OnCandidate(func(c Candidate) { if c == nil { candidateGatheredFunc() return } t.Log(c.NetworkType(), c.Priority(), c) })) - assert.NoError(t, a.GatherCandidates()) + require.NoError(t, a.GatherCandidates()) <-candidateGathered.Done() - assert.NoError(t, a.Close()) - assert.NoError(t, server.Close()) + require.NoError(t, a.Close()) + require.NoError(t, server.Close()) } // Assert that TURN gathering is done concurrently @@ -335,7 +334,7 @@ func TestTURNConcurrency(t *testing.T) { PacketConnConfigs: packetConnConfigs, ListenerConfigs: listenerConfigs, }) - assert.NoError(t, err) + require.NoError(t, err) urls := []*stun.URI{} for i := 0; i <= 10; i++ { @@ -363,26 +362,26 @@ func TestTURNConcurrency(t *testing.T) { NetworkTypes: supportedNetworkTypes(), Urls: urls, }) - assert.NoError(t, err) + require.NoError(t, err) candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) - assert.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, a.OnCandidate(func(c Candidate) { if c != nil { candidateGatheredFunc() } })) - assert.NoError(t, a.GatherCandidates()) + require.NoError(t, a.GatherCandidates()) <-candidateGathered.Done() - assert.NoError(t, a.Close()) - assert.NoError(t, server.Close()) + require.NoError(t, a.Close()) + require.NoError(t, server.Close()) } t.Run("UDP Relay", func(t *testing.T) { serverPort := randomPort(t) serverListener, err := net.ListenPacket("udp", localhostIPStr+":"+strconv.Itoa(serverPort)) - assert.NoError(t, err) + require.NoError(t, err) runTest(stun.ProtoTypeUDP, stun.SchemeTypeTURN, serverListener, nil, serverPort) }) @@ -390,33 +389,33 @@ func TestTURNConcurrency(t *testing.T) { t.Run("TCP Relay", func(t *testing.T) { serverPort := randomPort(t) serverListener, err := net.Listen("tcp", localhostIPStr+":"+strconv.Itoa(serverPort)) - assert.NoError(t, err) + require.NoError(t, err) runTest(stun.ProtoTypeTCP, stun.SchemeTypeTURN, nil, serverListener, serverPort) }) t.Run("TLS Relay", func(t *testing.T) { certificate, genErr := selfsign.GenerateSelfSigned() - assert.NoError(t, genErr) + require.NoError(t, genErr) serverPort := randomPort(t) serverListener, err := tls.Listen("tcp", localhostIPStr+":"+strconv.Itoa(serverPort), &tls.Config{ //nolint:gosec Certificates: []tls.Certificate{certificate}, }) - assert.NoError(t, err) + require.NoError(t, err) runTest(stun.ProtoTypeTCP, stun.SchemeTypeTURNS, nil, serverListener, serverPort) }) t.Run("DTLS Relay", func(t *testing.T) { certificate, genErr := selfsign.GenerateSelfSigned() - assert.NoError(t, genErr) + require.NoError(t, genErr) serverPort := randomPort(t) serverListener, err := dtls.Listen("udp", &net.UDPAddr{IP: net.ParseIP(localhostIPStr), Port: serverPort}, &dtls.Config{ Certificates: []tls.Certificate{certificate}, }) - assert.NoError(t, err) + require.NoError(t, err) runTest(stun.ProtoTypeUDP, stun.SchemeTypeTURNS, nil, serverListener, serverPort) }) @@ -432,7 +431,7 @@ func TestSTUNTURNConcurrency(t *testing.T) { serverPort := randomPort(t) serverListener, err := net.ListenPacket("udp4", localhostIPStr+":"+strconv.Itoa(serverPort)) - assert.NoError(t, err) + require.NoError(t, err) server, err := turn.NewServer(turn.ServerConfig{ Realm: "pion.ly", @@ -444,7 +443,7 @@ func TestSTUNTURNConcurrency(t *testing.T) { }, }, }) - assert.NoError(t, err) + require.NoError(t, err) urls := []*stun.URI{} for i := 0; i <= 10; i++ { @@ -468,25 +467,25 @@ func TestSTUNTURNConcurrency(t *testing.T) { Urls: urls, CandidateTypes: []CandidateType{CandidateTypeServerReflexive, CandidateTypeRelay}, }) - assert.NoError(t, err) + require.NoError(t, err) { gatherLim := test.TimeOut(time.Second * 3) // As TURN and STUN should be checked in parallel, this should complete before the default STUN timeout (5s) candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) - assert.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, a.OnCandidate(func(c Candidate) { if c != nil { candidateGatheredFunc() } })) - assert.NoError(t, a.GatherCandidates()) + require.NoError(t, a.GatherCandidates()) <-candidateGathered.Done() gatherLim.Stop() } - assert.NoError(t, a.Close()) - assert.NoError(t, server.Close()) + require.NoError(t, a.Close()) + require.NoError(t, server.Close()) } // Assert that srflx candidates can be gathered from TURN servers @@ -504,7 +503,7 @@ func TestTURNSrflx(t *testing.T) { serverPort := randomPort(t) serverListener, err := net.ListenPacket("udp4", localhostIPStr+":"+strconv.Itoa(serverPort)) - assert.NoError(t, err) + require.NoError(t, err) server, err := turn.NewServer(turn.ServerConfig{ Realm: "pion.ly", @@ -516,7 +515,7 @@ func TestTURNSrflx(t *testing.T) { }, }, }) - assert.NoError(t, err) + require.NoError(t, err) urls := []*stun.URI{{ Scheme: stun.SchemeTypeTURN, @@ -532,33 +531,33 @@ func TestTURNSrflx(t *testing.T) { Urls: urls, CandidateTypes: []CandidateType{CandidateTypeServerReflexive, CandidateTypeRelay}, }) - assert.NoError(t, err) + require.NoError(t, err) candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) - assert.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, a.OnCandidate(func(c Candidate) { if c != nil && c.Type() == CandidateTypeServerReflexive { candidateGatheredFunc() } })) - assert.NoError(t, a.GatherCandidates()) + require.NoError(t, a.GatherCandidates()) <-candidateGathered.Done() - assert.NoError(t, a.Close()) - assert.NoError(t, server.Close()) + require.NoError(t, a.Close()) + require.NoError(t, server.Close()) } func TestCloseConnLog(t *testing.T) { a, err := NewAgent(&AgentConfig{}) - assert.NoError(t, err) + require.NoError(t, err) closeConnAndLog(nil, a.log, "normal nil") var nc *net.UDPConn closeConnAndLog(nc, a.log, "nil ptr") - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) } type mockProxy struct { @@ -594,10 +593,10 @@ func TestTURNProxyDialer(t *testing.T) { }) tcpProxyURI, err := url.Parse("tcp://fakeproxy:3128") - assert.NoError(t, err) + require.NoError(t, err) proxyDialer, err := proxy.FromURL(tcpProxyURI, proxy.Direct) - assert.NoError(t, err) + require.NoError(t, err) a, err := NewAgent(&AgentConfig{ CandidateTypes: []CandidateType{CandidateTypeRelay}, @@ -614,23 +613,23 @@ func TestTURNProxyDialer(t *testing.T) { }, ProxyDialer: proxyDialer, }) - assert.NoError(t, err) + require.NoError(t, err) candidateGatherFinish, candidateGatherFinishFunc := context.WithCancel(context.Background()) - assert.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, a.OnCandidate(func(c Candidate) { if c == nil { candidateGatherFinishFunc() } })) - assert.NoError(t, a.GatherCandidates()) + require.NoError(t, a.GatherCandidates()) <-candidateGatherFinish.Done() <-proxyWasDialed.Done() - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) } -// TestUDPMuxDefaultWithNAT1To1IPsUsage asserts that candidates +// TestUDPMuxDefaultWithNAT1To1IPsUsage requires that candidates // are given and connections are valid when using UDPMuxDefault and NAT1To1IPs. func TestUDPMuxDefaultWithNAT1To1IPsUsage(t *testing.T) { report := test.CheckRoutines(t) @@ -640,7 +639,7 @@ func TestUDPMuxDefaultWithNAT1To1IPsUsage(t *testing.T) { defer lim.Stop() conn, err := net.ListenPacket("udp4", ":0") - assert.NoError(t, err) + require.NoError(t, err) defer func() { _ = conn.Close() }() @@ -657,22 +656,22 @@ func TestUDPMuxDefaultWithNAT1To1IPsUsage(t *testing.T) { NAT1To1IPCandidateType: CandidateTypeHost, UDPMux: mux, }) - assert.NoError(t, err) + require.NoError(t, err) gatherCandidateDone := make(chan struct{}) - assert.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, a.OnCandidate(func(c Candidate) { if c == nil { close(gatherCandidateDone) } else { - assert.Equal(t, "1.2.3.4", c.Address()) + require.Equal(t, "1.2.3.4", c.Address()) } })) - assert.NoError(t, a.GatherCandidates()) + require.NoError(t, a.GatherCandidates()) <-gatherCandidateDone - assert.NotEqual(t, 0, len(mux.connsIPv4)) + require.NotEqual(t, 0, len(mux.connsIPv4)) - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) } // Assert that candidates are given for each mux in a MultiUDPMux @@ -688,7 +687,7 @@ func TestMultiUDPMuxUsage(t *testing.T) { for i := 0; i < 3; i++ { port := randomPort(t) conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IP{127, 0, 0, 1}, Port: port}) - assert.NoError(t, err) + require.NoError(t, err) defer func() { _ = conn.Close() }() @@ -707,29 +706,29 @@ func TestMultiUDPMuxUsage(t *testing.T) { CandidateTypes: []CandidateType{CandidateTypeHost}, UDPMux: NewMultiUDPMuxDefault(udpMuxInstances...), }) - assert.NoError(t, err) + require.NoError(t, err) candidateCh := make(chan Candidate) - assert.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, a.OnCandidate(func(c Candidate) { if c == nil { close(candidateCh) return } candidateCh <- c })) - assert.NoError(t, a.GatherCandidates()) + require.NoError(t, a.GatherCandidates()) portFound := make(map[int]bool) for c := range candidateCh { portFound[c.Port()] = true - assert.True(t, c.NetworkType().IsUDP(), "All candidates should be UDP") + require.True(t, c.NetworkType().IsUDP(), "All candidates should be UDP") } - assert.Len(t, portFound, len(expectedPorts)) + require.Len(t, portFound, len(expectedPorts)) for _, port := range expectedPorts { - assert.True(t, portFound[port], "There should be a candidate for each UDP mux port") + require.True(t, portFound[port], "There should be a candidate for each UDP mux port") } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) } // Assert that candidates are given for each mux in a MultiTCPMux @@ -748,7 +747,7 @@ func TestMultiTCPMuxUsage(t *testing.T) { IP: net.IP{127, 0, 0, 1}, Port: port, }) - assert.NoError(t, err) + require.NoError(t, err) defer func() { _ = listener.Close() }() @@ -765,17 +764,17 @@ func TestMultiTCPMuxUsage(t *testing.T) { CandidateTypes: []CandidateType{CandidateTypeHost}, TCPMux: NewMultiTCPMuxDefault(tcpMuxInstances...), }) - assert.NoError(t, err) + require.NoError(t, err) candidateCh := make(chan Candidate) - assert.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, a.OnCandidate(func(c Candidate) { if c == nil { close(candidateCh) return } candidateCh <- c })) - assert.NoError(t, a.GatherCandidates()) + require.NoError(t, a.GatherCandidates()) portFound := make(map[int]bool) for c := range candidateCh { @@ -784,12 +783,12 @@ func TestMultiTCPMuxUsage(t *testing.T) { portFound[c.Port()] = true } } - assert.Len(t, portFound, len(expectedPorts)) + require.Len(t, portFound, len(expectedPorts)) for _, port := range expectedPorts { - assert.True(t, portFound[port], "There should be a candidate for each TCP mux port") + require.True(t, portFound[port], "There should be a candidate for each TCP mux port") } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) } // Assert that UniversalUDPMux is used while gathering when configured in the Agent @@ -801,7 +800,7 @@ func TestUniversalUDPMuxUsage(t *testing.T) { defer lim.Stop() conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IP{127, 0, 0, 1}, Port: randomPort(t)}) - assert.NoError(t, err) + require.NoError(t, err) defer func() { _ = conn.Close() }() @@ -826,27 +825,27 @@ func TestUniversalUDPMuxUsage(t *testing.T) { CandidateTypes: []CandidateType{CandidateTypeServerReflexive}, UDPMuxSrflx: udpMuxSrflx, }) - assert.NoError(t, err) + require.NoError(t, err) candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) - assert.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, a.OnCandidate(func(c Candidate) { if c == nil { candidateGatheredFunc() return } t.Log(c.NetworkType(), c.Priority(), c) })) - assert.NoError(t, a.GatherCandidates()) + require.NoError(t, a.GatherCandidates()) <-candidateGathered.Done() - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) // Twice because of 2 STUN servers configured - assert.Equal(t, numSTUNS, udpMuxSrflx.getXORMappedAddrUsedTimes, "expected times that GetXORMappedAddr should be called") + require.Equal(t, numSTUNS, udpMuxSrflx.getXORMappedAddrUsedTimes, "expected times that GetXORMappedAddr should be called") // One for Restart() when agent has been initialized and one time when Close() the agent - assert.Equal(t, 2, udpMuxSrflx.removeConnByUfragTimes, "expected times that RemoveConnByUfrag should be called") + require.Equal(t, 2, udpMuxSrflx.removeConnByUfragTimes, "expected times that RemoveConnByUfrag should be called") // Twice because of 2 STUN servers configured - assert.Equal(t, numSTUNS, udpMuxSrflx.getConnForURLTimes, "expected times that GetConnForURL should be called") + require.Equal(t, numSTUNS, udpMuxSrflx.getConnForURLTimes, "expected times that GetConnForURL should be called") } type universalUDPMuxMock struct { diff --git a/gather_vnet_test.go b/gather_vnet_test.go index 5410bc2..38dea08 100644 --- a/gather_vnet_test.go +++ b/gather_vnet_test.go @@ -17,7 +17,7 @@ import ( "github.com/pion/stun/v2" "github.com/pion/transport/v3/test" "github.com/pion/transport/v3/vnet" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestVNetGather(t *testing.T) { @@ -28,12 +28,12 @@ func TestVNetGather(t *testing.T) { t.Run("No local IP address", func(t *testing.T) { n, err := vnet.NewNet(&vnet.NetConfig{}) - assert.NoError(t, err) + require.NoError(t, err) a, err := NewAgent(&AgentConfig{ Net: n, }) - assert.NoError(t, err) + require.NoError(t, err) localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) if len(localIPs) > 0 { @@ -42,7 +42,7 @@ func TestVNetGather(t *testing.T) { t.Fatal(err) } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) }) t.Run("Gather a dynamic IP address", func(t *testing.T) { @@ -73,7 +73,7 @@ func TestVNetGather(t *testing.T) { a, err := NewAgent(&AgentConfig{ Net: nw, }) - assert.NoError(t, err) + require.NoError(t, err) localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) if len(localIPs) == 0 { @@ -91,7 +91,7 @@ func TestVNetGather(t *testing.T) { } } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) }) t.Run("listenUDP", func(t *testing.T) { @@ -157,8 +157,8 @@ func TestVNetGather(t *testing.T) { t.Fatalf("listenUDP with port restriction of 5000 listened on incorrect port (%s)", port) } - assert.NoError(t, conn.Close()) - assert.NoError(t, a.Close()) + require.NoError(t, conn.Close()) + require.NoError(t, a.Close()) }) } @@ -181,7 +181,7 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { CIDR: "1.2.3.0/24", LoggerFactory: loggerFactory, }) - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") lan, err := vnet.NewRouter(&vnet.RouterConfig{ CIDR: "10.0.0.0/24", @@ -191,10 +191,10 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { }, LoggerFactory: loggerFactory, }) - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") err = wan.AddRouter(lan) - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") nw, err := vnet.NewNet(&vnet.NetConfig{ StaticIPs: []string{localIP0, localIP1}, @@ -204,7 +204,7 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { } err = lan.AddNet(nw) - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") a, err := NewAgent(&AgentConfig{ NetworkTypes: []NetworkType{ @@ -213,7 +213,7 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { NAT1To1IPs: []string{map0, map1}, Net: nw, }) - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") defer a.Close() //nolint:errcheck done := make(chan struct{}) @@ -222,17 +222,17 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { close(done) } }) - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") err = a.GatherCandidates() - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") log.Debug("Wait until gathering is complete...") <-done log.Debug("Gathering is done") candidates, err := a.GetLocalCandidates() - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") if len(candidates) != 2 { t.Fatal("There must be two candidates") @@ -274,7 +274,7 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { CIDR: "1.2.3.0/24", LoggerFactory: loggerFactory, }) - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") lan, err := vnet.NewRouter(&vnet.RouterConfig{ CIDR: "10.0.0.0/24", @@ -286,10 +286,10 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { }, LoggerFactory: loggerFactory, }) - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") err = wan.AddRouter(lan) - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") nw, err := vnet.NewNet(&vnet.NetConfig{ StaticIPs: []string{ @@ -301,7 +301,7 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { } err = lan.AddNet(nw) - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") a, err := NewAgent(&AgentConfig{ NetworkTypes: []NetworkType{ @@ -313,7 +313,7 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { NAT1To1IPCandidateType: CandidateTypeServerReflexive, Net: nw, }) - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") defer a.Close() //nolint:errcheck done := make(chan struct{}) @@ -322,17 +322,17 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { close(done) } }) - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") err = a.GatherCandidates() - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") log.Debug("Wait until gathering is complete...") <-done log.Debug("Gathering is done") candidates, err := a.GetLocalCandidates() - assert.NoError(t, err, "should succeed") + require.NoError(t, err, "should succeed") if len(candidates) != 2 { t.Fatalf("Expected two candidates. actually %d", len(candidates)) @@ -352,10 +352,10 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { } } - assert.NotNil(t, candiHost, "should not be nil") - assert.Equal(t, "10.0.0.1", candiHost.Address(), "should match") - assert.NotNil(t, candiSrflx, "should not be nil") - assert.Equal(t, "1.2.3.4", candiSrflx.Address(), "should match") + require.NotNil(t, candiHost, "should not be nil") + require.Equal(t, "10.0.0.1", candiHost.Address(), "should match") + require.NotNil(t, candiSrflx, "should not be nil") + require.Equal(t, "1.2.3.4", candiSrflx.Address(), "should match") }) } @@ -385,11 +385,11 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { a, err := NewAgent(&AgentConfig{ Net: nw, InterfaceFilter: func(interfaceName string) bool { - assert.Equal(t, "eth0", interfaceName) + require.Equal(t, "eth0", interfaceName) return false }, }) - assert.NoError(t, err) + require.NoError(t, err) localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) if err != nil { @@ -398,18 +398,18 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { t.Fatal("InterfaceFilter should have excluded everything") } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) }) t.Run("IPFilter should exclude the IP", func(t *testing.T) { a, err := NewAgent(&AgentConfig{ Net: nw, IPFilter: func(ip net.IP) bool { - assert.Equal(t, net.IP{1, 2, 3, 1}, ip) + require.Equal(t, net.IP{1, 2, 3, 1}, ip) return false }, }) - assert.NoError(t, err) + require.NoError(t, err) localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) if err != nil { @@ -418,18 +418,18 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { t.Fatal("IPFilter should have excluded everything") } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) }) t.Run("InterfaceFilter should not exclude the interface", func(t *testing.T) { a, err := NewAgent(&AgentConfig{ Net: nw, InterfaceFilter: func(interfaceName string) bool { - assert.Equal(t, "eth0", interfaceName) + require.Equal(t, "eth0", interfaceName) return true }, }) - assert.NoError(t, err) + require.NoError(t, err) localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) if err != nil { @@ -438,7 +438,7 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { t.Fatal("InterfaceFilter should not have excluded anything") } - assert.NoError(t, a.Close()) + require.NoError(t, a.Close()) }) } @@ -462,9 +462,7 @@ func TestVNetGather_TURNConnectionLeak(t *testing.T) { } v, err := buildVNet(natType, natType) - if !assert.NoError(t, err, "should succeed") { - return - } + require.NoError(t, err, "should succeed") defer v.close() cfg0 := &AgentConfig{ @@ -477,11 +475,9 @@ func TestVNetGather_TURNConnectionLeak(t *testing.T) { Net: v.net0, } aAgent, err := NewAgent(cfg0) - if !assert.NoError(t, err, "should succeed") { - return - } + require.NoError(t, err, "should succeed") aAgent.gatherCandidatesRelay(context.Background(), []*stun.URI{turnServerURL}) // Assert relay conn leak on close. - assert.NoError(t, aAgent.Close()) + require.NoError(t, aAgent.Close()) } diff --git a/ice_test.go b/ice_test.go index 5740cd1..a88f309 100644 --- a/ice_test.go +++ b/ice_test.go @@ -6,7 +6,7 @@ package ice import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestConnectedState_String(t *testing.T) { @@ -25,7 +25,7 @@ func TestConnectedState_String(t *testing.T) { } for i, testCase := range testCases { - assert.Equal(t, + require.Equal(t, testCase.expectedString, testCase.connectionState.String(), "testCase: %d %v", i, testCase, @@ -45,7 +45,7 @@ func TestGatheringState_String(t *testing.T) { } for i, testCase := range testCases { - assert.Equal(t, + require.Equal(t, testCase.expectedString, testCase.gatheringState.String(), "testCase: %d %v", i, testCase, diff --git a/mdns_test.go b/mdns_test.go index 7cab077..0899c31 100644 --- a/mdns_test.go +++ b/mdns_test.go @@ -13,7 +13,7 @@ import ( "time" "github.com/pion/transport/v3/test" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestMulticastDNSOnlyConnection(t *testing.T) { @@ -54,8 +54,8 @@ func TestMulticastDNSOnlyConnection(t *testing.T) { <-aConnected <-bConnected - assert.NoError(t, aAgent.Close()) - assert.NoError(t, bAgent.Close()) + require.NoError(t, aAgent.Close()) + require.NoError(t, bAgent.Close()) } func TestMulticastDNSMixedConnection(t *testing.T) { @@ -98,8 +98,8 @@ func TestMulticastDNSMixedConnection(t *testing.T) { <-aConnected <-bConnected - assert.NoError(t, aAgent.Close()) - assert.NoError(t, bAgent.Close()) + require.NoError(t, aAgent.Close()) + require.NoError(t, bAgent.Close()) } func TestMulticastDNSStaticHostName(t *testing.T) { @@ -115,7 +115,7 @@ func TestMulticastDNSStaticHostName(t *testing.T) { MulticastDNSMode: MulticastDNSModeQueryAndGather, MulticastDNSHostName: "invalidHostName", }) - assert.Equal(t, err, ErrInvalidMulticastDNSHostName) + require.Equal(t, err, ErrInvalidMulticastDNSHostName) agent, err := NewAgent(&AgentConfig{ NetworkTypes: []NetworkType{NetworkTypeUDP4}, @@ -123,18 +123,18 @@ func TestMulticastDNSStaticHostName(t *testing.T) { MulticastDNSMode: MulticastDNSModeQueryAndGather, MulticastDNSHostName: "validName.local", }) - assert.NoError(t, err) + require.NoError(t, err) correctHostName, resolveFunc := context.WithCancel(context.Background()) - assert.NoError(t, agent.OnCandidate(func(c Candidate) { + require.NoError(t, agent.OnCandidate(func(c Candidate) { if c != nil && c.Address() == "validName.local" { resolveFunc() } })) - assert.NoError(t, agent.GatherCandidates()) + require.NoError(t, agent.GatherCandidates()) <-correctHostName.Done() - assert.NoError(t, agent.Close()) + require.NoError(t, agent.Close()) } func TestGenerateMulticastDNSName(t *testing.T) { diff --git a/net_test.go b/net_test.go index 5b32fbe..949fd81 100644 --- a/net_test.go +++ b/net_test.go @@ -7,7 +7,7 @@ import ( "net" "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestIsSupportedIPv6(t *testing.T) { @@ -37,8 +37,8 @@ func TestCreateAddr(t *testing.T) { ipv6 := net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} port := 9000 - assert.Equal(t, &net.UDPAddr{IP: ipv4, Port: port}, createAddr(NetworkTypeUDP4, ipv4, port)) - assert.Equal(t, &net.UDPAddr{IP: ipv6, Port: port}, createAddr(NetworkTypeUDP6, ipv6, port)) - assert.Equal(t, &net.TCPAddr{IP: ipv4, Port: port}, createAddr(NetworkTypeTCP4, ipv4, port)) - assert.Equal(t, &net.TCPAddr{IP: ipv6, Port: port}, createAddr(NetworkTypeTCP6, ipv6, port)) + require.Equal(t, &net.UDPAddr{IP: ipv4, Port: port}, createAddr(NetworkTypeUDP4, ipv4, port)) + require.Equal(t, &net.UDPAddr{IP: ipv6, Port: port}, createAddr(NetworkTypeUDP6, ipv6, port)) + require.Equal(t, &net.TCPAddr{IP: ipv4, Port: port}, createAddr(NetworkTypeTCP4, ipv4, port)) + require.Equal(t, &net.TCPAddr{IP: ipv6, Port: port}, createAddr(NetworkTypeTCP6, ipv6, port)) } diff --git a/networktype_test.go b/networktype_test.go index 201aadd..eb4a2e4 100644 --- a/networktype_test.go +++ b/networktype_test.go @@ -7,7 +7,7 @@ import ( "net" "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestNetworkTypeParsing_Success(t *testing.T) { @@ -79,15 +79,15 @@ func TestNetworkTypeParsing_Failure(t *testing.T) { } func TestNetworkTypeIsUDP(t *testing.T) { - assert.True(t, NetworkTypeUDP4.IsUDP()) - assert.True(t, NetworkTypeUDP6.IsUDP()) - assert.False(t, NetworkTypeUDP4.IsTCP()) - assert.False(t, NetworkTypeUDP6.IsTCP()) + require.True(t, NetworkTypeUDP4.IsUDP()) + require.True(t, NetworkTypeUDP6.IsUDP()) + require.False(t, NetworkTypeUDP4.IsTCP()) + require.False(t, NetworkTypeUDP6.IsTCP()) } func TestNetworkTypeIsTCP(t *testing.T) { - assert.True(t, NetworkTypeTCP4.IsTCP()) - assert.True(t, NetworkTypeTCP6.IsTCP()) - assert.False(t, NetworkTypeTCP4.IsUDP()) - assert.False(t, NetworkTypeTCP6.IsUDP()) + require.True(t, NetworkTypeTCP4.IsTCP()) + require.True(t, NetworkTypeTCP6.IsTCP()) + require.False(t, NetworkTypeTCP4.IsUDP()) + require.False(t, NetworkTypeTCP6.IsUDP()) } diff --git a/tcp_mux_multi_test.go b/tcp_mux_multi_test.go index 291a29d..ea664fc 100644 --- a/tcp_mux_multi_test.go +++ b/tcp_mux_multi_test.go @@ -14,7 +14,6 @@ import ( "github.com/pion/logging" "github.com/pion/stun/v2" "github.com/pion/transport/v3/test" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -77,9 +76,9 @@ func TestMultiTCPMux_Recv(t *testing.T) { recv := make([]byte, n) n2, rAddr, err := pktConn.ReadFrom(recv) require.NoError(t, err, "error receiving data") - assert.Equal(t, conn.LocalAddr(), rAddr, "remote TCP address mismatch") - assert.Equal(t, n, n2, "received byte size mismatch") - assert.Equal(t, msg.Raw, recv, "received bytes mismatch") + require.Equal(t, conn.LocalAddr(), rAddr, "remote TCP address mismatch") + require.Equal(t, n, n2, "received byte size mismatch") + require.Equal(t, msg.Raw, recv, "received bytes mismatch") // Check echo response n, err = pktConn.WriteTo(recv, conn.LocalAddr()) @@ -87,8 +86,8 @@ func TestMultiTCPMux_Recv(t *testing.T) { recvEcho := make([]byte, n) n3, err := readStreamingPacket(conn, recvEcho) require.NoError(t, err, "error receiving echo data") - assert.Equal(t, n2, n3, "received byte size mismatch") - assert.Equal(t, msg.Raw, recvEcho, "received bytes mismatch") + require.Equal(t, n2, n3, "received byte size mismatch") + require.Equal(t, msg.Raw, recvEcho, "received bytes mismatch") } }) } @@ -126,6 +125,6 @@ func TestMultiTCPMux_NoDeadlockWhenClosingUnusedPacketConn(t *testing.T) { require.NoError(t, muxMulti.Close(), "error closing tcpMux") conn, err := muxMulti.GetAllConns("test", false, net.IP{127, 0, 0, 1}) - assert.Nil(t, conn, "should receive nil because mux is closed") - assert.Equal(t, io.ErrClosedPipe, err, "should receive error because mux is closed") + require.Nil(t, conn, "should receive nil because mux is closed") + require.Equal(t, io.ErrClosedPipe, err, "should receive error because mux is closed") } diff --git a/tcp_mux_test.go b/tcp_mux_test.go index 0175b77..5e1875c 100644 --- a/tcp_mux_test.go +++ b/tcp_mux_test.go @@ -13,7 +13,6 @@ import ( "github.com/pion/logging" "github.com/pion/stun/v2" "github.com/pion/transport/v3/test" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -73,9 +72,9 @@ func TestTCPMux_Recv(t *testing.T) { recv := make([]byte, n) n2, rAddr, err := pktConn.ReadFrom(recv) require.NoError(t, err, "error receiving data") - assert.Equal(t, conn.LocalAddr(), rAddr, "remote tcp address mismatch") - assert.Equal(t, n, n2, "received byte size mismatch") - assert.Equal(t, msg.Raw, recv, "received bytes mismatch") + require.Equal(t, conn.LocalAddr(), rAddr, "remote tcp address mismatch") + require.Equal(t, n, n2, "received byte size mismatch") + require.Equal(t, msg.Raw, recv, "received bytes mismatch") // Check echo response n, err = pktConn.WriteTo(recv, conn.LocalAddr()) @@ -83,8 +82,8 @@ func TestTCPMux_Recv(t *testing.T) { recvEcho := make([]byte, n) n3, err := readStreamingPacket(conn, recvEcho) require.NoError(t, err, "error receiving echo data") - assert.Equal(t, n2, n3, "received byte size mismatch") - assert.Equal(t, msg.Raw, recvEcho, "received bytes mismatch") + require.Equal(t, n2, n3, "received byte size mismatch") + require.Equal(t, msg.Raw, recvEcho, "received bytes mismatch") }) } } @@ -120,8 +119,8 @@ func TestTCPMux_NoDeadlockWhenClosingUnusedPacketConn(t *testing.T) { require.NoError(t, tcpMux.Close(), "error closing tcpMux") conn, err := tcpMux.GetConnByUfrag("test", false, listener.Addr().(*net.TCPAddr).IP) - assert.Nil(t, conn, "should receive nil because mux is closed") - assert.Equal(t, io.ErrClosedPipe, err, "should receive error because mux is closed") + require.Nil(t, conn, "should receive nil because mux is closed") + require.Equal(t, io.ErrClosedPipe, err, "should receive error because mux is closed") } func TestTCPMux_FirstPacketTimeout(t *testing.T) { @@ -253,8 +252,8 @@ func TestTCPMux_NoLeakForConnectionFromStun(t *testing.T) { recv := make([]byte, n) n2, rAddr, err := pktConn.ReadFrom(recv) require.NoError(t, err, "error receiving data") - assert.Equal(t, conn.LocalAddr(), rAddr, "remote tcp address mismatch") - assert.Equal(t, n, n2, "received byte size mismatch") - assert.Equal(t, msg.Raw, recv, "received bytes mismatch") + require.Equal(t, conn.LocalAddr(), rAddr, "remote tcp address mismatch") + require.Equal(t, n, n2, "received byte size mismatch") + require.Equal(t, msg.Raw, recv, "received bytes mismatch") }) } diff --git a/tcptype_test.go b/tcptype_test.go index 8a35bf1..af075d5 100644 --- a/tcptype_test.go +++ b/tcptype_test.go @@ -6,21 +6,21 @@ package ice import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestTCPType(t *testing.T) { var tcpType TCPType - assert.Equal(t, TCPTypeUnspecified, tcpType) - assert.Equal(t, TCPTypeActive, NewTCPType("active")) - assert.Equal(t, TCPTypePassive, NewTCPType("passive")) - assert.Equal(t, TCPTypeSimultaneousOpen, NewTCPType("so")) - assert.Equal(t, TCPTypeUnspecified, NewTCPType("something else")) + require.Equal(t, TCPTypeUnspecified, tcpType) + require.Equal(t, TCPTypeActive, NewTCPType("active")) + require.Equal(t, TCPTypePassive, NewTCPType("passive")) + require.Equal(t, TCPTypeSimultaneousOpen, NewTCPType("so")) + require.Equal(t, TCPTypeUnspecified, NewTCPType("something else")) - assert.Equal(t, "", TCPTypeUnspecified.String()) - assert.Equal(t, "active", TCPTypeActive.String()) - assert.Equal(t, "passive", TCPTypePassive.String()) - assert.Equal(t, "so", TCPTypeSimultaneousOpen.String()) - assert.Equal(t, "Unknown", TCPType(-1).String()) + require.Equal(t, "", TCPTypeUnspecified.String()) + require.Equal(t, "active", TCPTypeActive.String()) + require.Equal(t, "passive", TCPTypePassive.String()) + require.Equal(t, "so", TCPTypeSimultaneousOpen.String()) + require.Equal(t, "Unknown", TCPType(-1).String()) } diff --git a/transport_vnet_test.go b/transport_vnet_test.go index e9e0ffc..3af3003 100644 --- a/transport_vnet_test.go +++ b/transport_vnet_test.go @@ -15,7 +15,7 @@ import ( "github.com/pion/stun/v2" "github.com/pion/transport/v3/test" "github.com/pion/transport/v3/vnet" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestRemoteLocalAddr(t *testing.T) { @@ -33,9 +33,7 @@ func TestRemoteLocalAddr(t *testing.T) { natType1 := &vnet.NATType{Mode: vnet.NATModeNAT1To1} v, errVnet := buildVNet(natType0, natType1) - if !assert.NoError(t, errVnet, "should succeed") { - return - } + require.NoError(t, errVnet, "should succeed") defer v.close() stunServerURL := &stun.URI{ @@ -47,13 +45,13 @@ func TestRemoteLocalAddr(t *testing.T) { t.Run("Disconnected Returns nil", func(t *testing.T) { disconnectedAgent, err := NewAgent(&AgentConfig{}) - assert.NoError(t, err) + require.NoError(t, err) disconnectedConn := Conn{agent: disconnectedAgent} - assert.Nil(t, disconnectedConn.RemoteAddr()) - assert.Nil(t, disconnectedConn.LocalAddr()) + require.Nil(t, disconnectedConn.RemoteAddr()) + require.Nil(t, disconnectedConn.LocalAddr()) - assert.NoError(t, disconnectedConn.Close()) + require.NoError(t, disconnectedConn.Close()) }) t.Run("Remote/Local Pair Match between Agents", func(t *testing.T) { @@ -72,27 +70,27 @@ func TestRemoteLocalAddr(t *testing.T) { bLAddr := cb.LocalAddr() // Assert that nothing is nil - assert.NotNil(t, aRAddr) - assert.NotNil(t, aLAddr) - assert.NotNil(t, bRAddr) - assert.NotNil(t, bLAddr) + require.NotNil(t, aRAddr) + require.NotNil(t, aLAddr) + require.NotNil(t, bRAddr) + require.NotNil(t, bLAddr) // Assert addresses - assert.Equal(t, aLAddr.String(), + require.Equal(t, aLAddr.String(), fmt.Sprintf("%s:%d", vnetLocalIPA, bRAddr.(*net.UDPAddr).Port), //nolint:forcetypeassert ) - assert.Equal(t, bLAddr.String(), + require.Equal(t, bLAddr.String(), fmt.Sprintf("%s:%d", vnetLocalIPB, aRAddr.(*net.UDPAddr).Port), //nolint:forcetypeassert ) - assert.Equal(t, aRAddr.String(), + require.Equal(t, aRAddr.String(), fmt.Sprintf("%s:%d", vnetGlobalIPB, bLAddr.(*net.UDPAddr).Port), //nolint:forcetypeassert ) - assert.Equal(t, bRAddr.String(), + require.Equal(t, bRAddr.String(), fmt.Sprintf("%s:%d", vnetGlobalIPA, aLAddr.(*net.UDPAddr).Port), //nolint:forcetypeassert ) // Close - assert.NoError(t, ca.Close()) - assert.NoError(t, cb.Close()) + require.NoError(t, ca.Close()) + require.NoError(t, cb.Close()) }) } From 01c35354b07b10903f3dfac64c883356958241a7 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Sat, 23 Mar 2024 20:08:57 -0400 Subject: [PATCH 016/114] Simplify usage of test.CheckRoutines() Execute directly instead of allocating function --- active_tcp_test.go | 6 +-- agent_handlers_test.go | 8 ++-- agent_test.go | 63 ++++++++++-------------------- agent_udpmux_test.go | 3 +- candidate_relay_test.go | 3 +- candidate_server_reflexive_test.go | 3 +- connectivity_vnet_test.go | 9 ++--- gather_test.go | 33 ++++++---------- gather_vnet_test.go | 12 ++---- mdns_test.go | 9 ++--- tcp_mux_multi_test.go | 6 +-- tcp_mux_test.go | 12 ++---- transport_test.go | 12 ++---- transport_vnet_test.go | 3 +- udp_mux_multi_test.go | 6 +-- udp_mux_test.go | 3 +- 16 files changed, 65 insertions(+), 126 deletions(-) diff --git a/active_tcp_test.go b/active_tcp_test.go index 69d73e9..39b9a35 100644 --- a/active_tcp_test.go +++ b/active_tcp_test.go @@ -35,8 +35,7 @@ func ipv6Available(t *testing.T) bool { } func TestActiveTCP(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 5) defer lim.Stop() @@ -163,8 +162,7 @@ func TestActiveTCP(t *testing.T) { // Assert that Active TCP connectivity isn't established inside // the main thread of the Agent func TestActiveTCP_NonBlocking(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 5) defer lim.Stop() diff --git a/agent_handlers_test.go b/agent_handlers_test.go index 675f853..025d473 100644 --- a/agent_handlers_test.go +++ b/agent_handlers_test.go @@ -12,8 +12,9 @@ import ( func TestConnectionStateNotifier(t *testing.T) { t.Run("TestManyUpdates", func(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + + defer test.CheckRoutines(t)() + updates := make(chan struct{}, 1) c := &handlerNotifier{ connectionStateFunc: func(_ ConnectionState) { @@ -40,8 +41,7 @@ func TestConnectionStateNotifier(t *testing.T) { <-done }) t.Run("TestUpdateOrdering", func(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() updates := make(chan ConnectionState) c := &handlerNotifier{ connectionStateFunc: func(cs ConnectionState) { diff --git a/agent_test.go b/agent_test.go index 851070f..dfb83f0 100644 --- a/agent_test.go +++ b/agent_test.go @@ -34,8 +34,7 @@ func (ba *BadAddr) String() string { } func TestHandlePeerReflexive(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks lim := test.TimeOut(time.Second * 2) @@ -183,8 +182,7 @@ func TestHandlePeerReflexive(t *testing.T) { // Assert that Agent on startup sends message, and doesn't wait for connectivityTicker to fire // https://github.com/pion/ice/issues/15 func TestConnectivityOnStartup(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -292,8 +290,7 @@ func TestConnectivityOnStartup(t *testing.T) { } func TestConnectivityLite(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -351,8 +348,7 @@ func TestConnectivityLite(t *testing.T) { } func TestInboundValidity(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() buildMsg := func(class stun.MessageClass, username, key string) *stun.Message { msg, err := stun.Build(stun.NewType(stun.MethodBinding, class), stun.TransactionID, @@ -507,8 +503,7 @@ func TestInboundValidity(t *testing.T) { } func TestInvalidAgentStarts(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() a, err := NewAgent(&AgentConfig{}) require.NoError(t, err) @@ -538,8 +533,7 @@ func TestInvalidAgentStarts(t *testing.T) { // Assert that Agent emits Connecting/Connected/Disconnected/Failed/Closed messages func TestConnectionStateCallback(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 5) defer lim.Stop() @@ -619,8 +613,7 @@ func TestInvalidGather(t *testing.T) { } func TestCandidatePairStats(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() // Avoid deadlocks? defer test.TimeOut(1 * time.Second).Stop() @@ -752,8 +745,7 @@ func TestCandidatePairStats(t *testing.T) { } func TestLocalCandidateStats(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() // Avoid deadlocks? defer test.TimeOut(1 * time.Second).Stop() @@ -833,8 +825,7 @@ func TestLocalCandidateStats(t *testing.T) { } func TestRemoteCandidateStats(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() // Avoid deadlocks? defer test.TimeOut(1 * time.Second).Stop() @@ -953,8 +944,7 @@ func TestRemoteCandidateStats(t *testing.T) { } func TestInitExtIPMapping(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() // a.extIPMapper should be nil by default a, err := NewAgent(&AgentConfig{}) @@ -1023,8 +1013,7 @@ func TestInitExtIPMapping(t *testing.T) { } func TestBindingRequestTimeout(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() const expectedRemovalCount = 2 @@ -1053,8 +1042,7 @@ func TestBindingRequestTimeout(t *testing.T) { // TestAgentCredentials checks if local username fragments and passwords (if set) meet RFC standard // and ensure it's backwards compatible with previous versions of the pion/ice func TestAgentCredentials(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() // Make sure to pass Travis check by disabling the logs log := logging.NewDefaultLoggerFactory() @@ -1084,8 +1072,7 @@ func TestAgentCredentials(t *testing.T) { // Assert that Agent on Failure deletes all existing candidates // User can then do an ICE Restart to bring agent back func TestConnectionStateFailedDeleteAllCandidates(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 5) defer lim.Stop() @@ -1130,8 +1117,7 @@ func TestConnectionStateFailedDeleteAllCandidates(t *testing.T) { // Assert that the ICE Agent can go directly from Connecting -> Failed on both sides func TestConnectionStateConnectingToFailed(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 5) defer lim.Stop() @@ -1190,8 +1176,7 @@ func TestConnectionStateConnectingToFailed(t *testing.T) { } func TestAgentRestart(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -1388,8 +1373,7 @@ func TestGetLocalCandidates(t *testing.T) { } func TestCloseInConnectionStateCallback(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 5) defer lim.Stop() @@ -1442,8 +1426,7 @@ func TestCloseInConnectionStateCallback(t *testing.T) { } func TestRunTaskInConnectionStateCallback(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 5) defer lim.Stop() @@ -1487,8 +1470,7 @@ func TestRunTaskInConnectionStateCallback(t *testing.T) { } func TestRunTaskInSelectedCandidatePairChangeCallback(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 5) defer lim.Stop() @@ -1540,8 +1522,7 @@ func TestRunTaskInSelectedCandidatePairChangeCallback(t *testing.T) { // Assert that a Lite agent goes to disconnected and failed func TestLiteLifecycle(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -1615,8 +1596,7 @@ func TestNilCandidatePair(t *testing.T) { } func TestGetSelectedCandidatePair(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -1673,8 +1653,7 @@ func TestGetSelectedCandidatePair(t *testing.T) { } func TestAcceptAggressiveNomination(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() diff --git a/agent_udpmux_test.go b/agent_udpmux_test.go index 524795d..8899da7 100644 --- a/agent_udpmux_test.go +++ b/agent_udpmux_test.go @@ -18,8 +18,7 @@ import ( // TestMuxAgent is an end to end test over UDP mux, ensuring two agents could connect over mux func TestMuxAgent(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() diff --git a/candidate_relay_test.go b/candidate_relay_test.go index cf832e4..e4a7352 100644 --- a/candidate_relay_test.go +++ b/candidate_relay_test.go @@ -27,8 +27,7 @@ func TestRelayOnlyConnection(t *testing.T) { lim := test.TimeOut(time.Second * 30) defer lim.Stop() - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() serverPort := randomPort(t) serverListener, err := net.ListenPacket("udp", localhostIPStr+":"+strconv.Itoa(serverPort)) diff --git a/candidate_server_reflexive_test.go b/candidate_server_reflexive_test.go index 5f2b162..3f4361d 100644 --- a/candidate_server_reflexive_test.go +++ b/candidate_server_reflexive_test.go @@ -19,8 +19,7 @@ import ( ) func TestServerReflexiveOnlyConnection(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks lim := test.TimeOut(time.Second * 30) diff --git a/connectivity_vnet_test.go b/connectivity_vnet_test.go index 99bafa5..2aa48f4 100644 --- a/connectivity_vnet_test.go +++ b/connectivity_vnet_test.go @@ -298,8 +298,7 @@ func closePipe(t *testing.T, ca *Conn, cb *Conn) { } func TestConnectivityVNet(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() stunServerURL := &stun.URI{ Scheme: stun.SchemeTypeSTUN, @@ -449,8 +448,7 @@ func TestConnectivityVNet(t *testing.T) { // TestDisconnectedToConnected requires that an agent can go to disconnected, and then return to connected successfully func TestDisconnectedToConnected(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 10) defer lim.Stop() @@ -547,8 +545,7 @@ func TestDisconnectedToConnected(t *testing.T) { // Agent.Write should use the best valid pair if a selected pair is not yet available func TestWriteUseValidPair(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 10) defer lim.Stop() diff --git a/gather_test.go b/gather_test.go index e070bb8..554d515 100644 --- a/gather_test.go +++ b/gather_test.go @@ -90,8 +90,7 @@ func TestListenUDP(t *testing.T) { } func TestGatherConcurrency(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -118,8 +117,7 @@ func TestGatherConcurrency(t *testing.T) { } func TestLoopbackCandidate(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -229,8 +227,7 @@ func TestLoopbackCandidate(t *testing.T) { // Assert that STUN gathering is done concurrently func TestSTUNConcurrency(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -305,8 +302,7 @@ func TestSTUNConcurrency(t *testing.T) { // Assert that TURN gathering is done concurrently func TestTURNConcurrency(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -423,8 +419,7 @@ func TestTURNConcurrency(t *testing.T) { // Assert that STUN and TURN gathering are done concurrently func TestSTUNTURNConcurrency(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 8) defer lim.Stop() @@ -495,8 +490,7 @@ func TestSTUNTURNConcurrency(t *testing.T) { // // https://tools.ietf.org/html/rfc5245#section-2.1 func TestTURNSrflx(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -581,8 +575,7 @@ func (m *mockProxy) Dial(string, string) (net.Conn, error) { } func TestTURNProxyDialer(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -632,8 +625,7 @@ func TestTURNProxyDialer(t *testing.T) { // TestUDPMuxDefaultWithNAT1To1IPsUsage requires that candidates // are given and connections are valid when using UDPMuxDefault and NAT1To1IPs. func TestUDPMuxDefaultWithNAT1To1IPsUsage(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -676,8 +668,7 @@ func TestUDPMuxDefaultWithNAT1To1IPsUsage(t *testing.T) { // Assert that candidates are given for each mux in a MultiUDPMux func TestMultiUDPMuxUsage(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -733,8 +724,7 @@ func TestMultiUDPMuxUsage(t *testing.T) { // Assert that candidates are given for each mux in a MultiTCPMux func TestMultiTCPMuxUsage(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -793,8 +783,7 @@ func TestMultiTCPMuxUsage(t *testing.T) { // Assert that UniversalUDPMux is used while gathering when configured in the Agent func TestUniversalUDPMuxUsage(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() diff --git a/gather_vnet_test.go b/gather_vnet_test.go index 38dea08..30ee32f 100644 --- a/gather_vnet_test.go +++ b/gather_vnet_test.go @@ -21,8 +21,7 @@ import ( ) func TestVNetGather(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() loggerFactory := logging.NewDefaultLoggerFactory() @@ -163,8 +162,7 @@ func TestVNetGather(t *testing.T) { } func TestVNetGatherWithNAT1To1(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() loggerFactory := logging.NewDefaultLoggerFactory() log := loggerFactory.NewLogger("test") @@ -360,8 +358,7 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { } func TestVNetGatherWithInterfaceFilter(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() loggerFactory := logging.NewDefaultLoggerFactory() r, err := vnet.NewRouter(&vnet.RouterConfig{ @@ -443,8 +440,7 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { } func TestVNetGather_TURNConnectionLeak(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() turnServerURL := &stun.URI{ Scheme: stun.SchemeTypeTURN, diff --git a/mdns_test.go b/mdns_test.go index 0899c31..48eeb59 100644 --- a/mdns_test.go +++ b/mdns_test.go @@ -17,8 +17,7 @@ import ( ) func TestMulticastDNSOnlyConnection(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks lim := test.TimeOut(time.Second * 30) @@ -59,8 +58,7 @@ func TestMulticastDNSOnlyConnection(t *testing.T) { } func TestMulticastDNSMixedConnection(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks lim := test.TimeOut(time.Second * 30) @@ -103,8 +101,7 @@ func TestMulticastDNSMixedConnection(t *testing.T) { } func TestMulticastDNSStaticHostName(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() diff --git a/tcp_mux_multi_test.go b/tcp_mux_multi_test.go index ea664fc..5630689 100644 --- a/tcp_mux_multi_test.go +++ b/tcp_mux_multi_test.go @@ -24,8 +24,7 @@ func TestMultiTCPMux_Recv(t *testing.T) { } { bufSize := bufSize t.Run(name, func(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() loggerFactory := logging.NewDefaultLoggerFactory() @@ -94,8 +93,7 @@ func TestMultiTCPMux_Recv(t *testing.T) { } func TestMultiTCPMux_NoDeadlockWhenClosingUnusedPacketConn(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() loggerFactory := logging.NewDefaultLoggerFactory() diff --git a/tcp_mux_test.go b/tcp_mux_test.go index 5e1875c..c6d9748 100644 --- a/tcp_mux_test.go +++ b/tcp_mux_test.go @@ -25,8 +25,7 @@ func TestTCPMux_Recv(t *testing.T) { } { bufSize := bufSize t.Run(name, func(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() loggerFactory := logging.NewDefaultLoggerFactory() @@ -89,8 +88,7 @@ func TestTCPMux_Recv(t *testing.T) { } func TestTCPMux_NoDeadlockWhenClosingUnusedPacketConn(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() loggerFactory := logging.NewDefaultLoggerFactory() @@ -124,8 +122,7 @@ func TestTCPMux_NoDeadlockWhenClosingUnusedPacketConn(t *testing.T) { } func TestTCPMux_FirstPacketTimeout(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() loggerFactory := logging.NewDefaultLoggerFactory() @@ -162,8 +159,7 @@ func TestTCPMux_FirstPacketTimeout(t *testing.T) { } func TestTCPMux_NoLeakForConnectionFromStun(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() loggerFactory := logging.NewDefaultLoggerFactory() diff --git a/transport_test.go b/transport_test.go index 9f0abd1..80a6bef 100644 --- a/transport_test.go +++ b/transport_test.go @@ -19,8 +19,7 @@ import ( func TestStressDuplex(t *testing.T) { // Check for leaking routines - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks lim := test.TimeOut(time.Second * 20) @@ -76,8 +75,7 @@ func TestTimeout(t *testing.T) { } // Check for leaking routines - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks lim := test.TimeOut(time.Second * 20) @@ -108,8 +106,7 @@ func TestTimeout(t *testing.T) { func TestReadClosed(t *testing.T) { // Check for leaking routines - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks lim := test.TimeOut(time.Second * 20) @@ -321,8 +318,7 @@ func randomPort(t testing.TB) int { func TestConnStats(t *testing.T) { // Check for leaking routines - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks lim := test.TimeOut(time.Second * 20) diff --git a/transport_vnet_test.go b/transport_vnet_test.go index 3af3003..ff27ec6 100644 --- a/transport_vnet_test.go +++ b/transport_vnet_test.go @@ -20,8 +20,7 @@ import ( func TestRemoteLocalAddr(t *testing.T) { // Check for leaking routines - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks lim := test.TimeOut(time.Second * 20) diff --git a/udp_mux_multi_test.go b/udp_mux_multi_test.go index a38702c..2f250f9 100644 --- a/udp_mux_multi_test.go +++ b/udp_mux_multi_test.go @@ -18,8 +18,7 @@ import ( ) func TestMultiUDPMux(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -111,8 +110,7 @@ func testMultiUDPMuxConnections(t *testing.T, udpMuxMulti *MultiUDPMuxDefault, u } func TestUnspecifiedUDPMux(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() diff --git a/udp_mux_test.go b/udp_mux_test.go index 01f52c5..253d38e 100644 --- a/udp_mux_test.go +++ b/udp_mux_test.go @@ -21,8 +21,7 @@ import ( ) func TestUDPMux(t *testing.T) { - report := test.CheckRoutines(t) - defer report() + defer test.CheckRoutines(t)() lim := test.TimeOut(time.Second * 30) defer lim.Stop() From 85a3a7f52407586fea6957187c461aaa08241611 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Sat, 23 Mar 2024 20:28:47 -0400 Subject: [PATCH 017/114] Simplify usage of test.TimeOut() Execute directly instead of allocating function --- active_tcp_test.go | 6 ++--- agent_handlers_test.go | 1 - agent_test.go | 39 ++++++++++-------------------- agent_udpmux_test.go | 3 +-- candidate_relay_test.go | 3 +-- candidate_server_reflexive_test.go | 3 +-- connectivity_vnet_test.go | 6 ++--- gather_test.go | 37 +++++++++------------------- mdns_test.go | 9 +++---- transport_test.go | 12 +++------ transport_vnet_test.go | 3 +-- udp_mux_multi_test.go | 6 ++--- udp_mux_test.go | 3 +-- 13 files changed, 43 insertions(+), 88 deletions(-) diff --git a/active_tcp_test.go b/active_tcp_test.go index 39b9a35..3df2c5e 100644 --- a/active_tcp_test.go +++ b/active_tcp_test.go @@ -37,8 +37,7 @@ func ipv6Available(t *testing.T) bool { func TestActiveTCP(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 5) - defer lim.Stop() + defer test.TimeOut(time.Second * 5).Stop() const listenPort = 7686 type testCase struct { @@ -164,8 +163,7 @@ func TestActiveTCP(t *testing.T) { func TestActiveTCP_NonBlocking(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 5) - defer lim.Stop() + defer test.TimeOut(time.Second * 5).Stop() cfg := &AgentConfig{ NetworkTypes: supportedNetworkTypes(), diff --git a/agent_handlers_test.go b/agent_handlers_test.go index 025d473..35680ee 100644 --- a/agent_handlers_test.go +++ b/agent_handlers_test.go @@ -12,7 +12,6 @@ import ( func TestConnectionStateNotifier(t *testing.T) { t.Run("TestManyUpdates", func(t *testing.T) { - defer test.CheckRoutines(t)() updates := make(chan struct{}, 1) diff --git a/agent_test.go b/agent_test.go index dfb83f0..0207476 100644 --- a/agent_test.go +++ b/agent_test.go @@ -37,8 +37,7 @@ func TestHandlePeerReflexive(t *testing.T) { defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks - lim := test.TimeOut(time.Second * 2) - defer lim.Stop() + defer test.TimeOut(time.Second * 2).Stop() t.Run("UDP prflx candidate from handleInbound()", func(t *testing.T) { a, err := NewAgent(&AgentConfig{}) @@ -184,8 +183,7 @@ func TestHandlePeerReflexive(t *testing.T) { func TestConnectivityOnStartup(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() // Create a network with two interfaces wan, err := vnet.NewRouter(&vnet.RouterConfig{ @@ -292,8 +290,7 @@ func TestConnectivityOnStartup(t *testing.T) { func TestConnectivityLite(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() stunServerURL := &stun.URI{ Scheme: SchemeTypeSTUN, @@ -535,8 +532,7 @@ func TestInvalidAgentStarts(t *testing.T) { func TestConnectionStateCallback(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 5) - defer lim.Stop() + defer test.TimeOut(time.Second * 5).Stop() disconnectedDuration := time.Second failedDuration := time.Second @@ -1074,8 +1070,7 @@ func TestAgentCredentials(t *testing.T) { func TestConnectionStateFailedDeleteAllCandidates(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 5) - defer lim.Stop() + defer test.TimeOut(time.Second * 5).Stop() oneSecond := time.Second KeepaliveInterval := time.Duration(0) @@ -1119,8 +1114,7 @@ func TestConnectionStateFailedDeleteAllCandidates(t *testing.T) { func TestConnectionStateConnectingToFailed(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 5) - defer lim.Stop() + defer test.TimeOut(time.Second * 5).Stop() oneSecond := time.Second KeepaliveInterval := time.Duration(0) @@ -1178,8 +1172,7 @@ func TestConnectionStateConnectingToFailed(t *testing.T) { func TestAgentRestart(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() oneSecond := time.Second @@ -1375,8 +1368,7 @@ func TestGetLocalCandidates(t *testing.T) { func TestCloseInConnectionStateCallback(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 5) - defer lim.Stop() + defer test.TimeOut(time.Second * 5).Stop() disconnectedDuration := time.Second failedDuration := time.Second @@ -1428,8 +1420,7 @@ func TestCloseInConnectionStateCallback(t *testing.T) { func TestRunTaskInConnectionStateCallback(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 5) - defer lim.Stop() + defer test.TimeOut(time.Second * 5).Stop() oneSecond := time.Second KeepaliveInterval := time.Duration(0) @@ -1472,8 +1463,7 @@ func TestRunTaskInConnectionStateCallback(t *testing.T) { func TestRunTaskInSelectedCandidatePairChangeCallback(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 5) - defer lim.Stop() + defer test.TimeOut(time.Second * 5).Stop() oneSecond := time.Second KeepaliveInterval := time.Duration(0) @@ -1524,8 +1514,7 @@ func TestRunTaskInSelectedCandidatePairChangeCallback(t *testing.T) { func TestLiteLifecycle(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() aNotifier, aConnected := onConnected() @@ -1598,8 +1587,7 @@ func TestNilCandidatePair(t *testing.T) { func TestGetSelectedCandidatePair(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() wan, err := vnet.NewRouter(&vnet.RouterConfig{ CIDR: "0.0.0.0/0", @@ -1655,8 +1643,7 @@ func TestGetSelectedCandidatePair(t *testing.T) { func TestAcceptAggressiveNomination(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() // Create a network with two interfaces wan, err := vnet.NewRouter(&vnet.RouterConfig{ diff --git a/agent_udpmux_test.go b/agent_udpmux_test.go index 8899da7..70e7d25 100644 --- a/agent_udpmux_test.go +++ b/agent_udpmux_test.go @@ -20,8 +20,7 @@ import ( func TestMuxAgent(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() const muxPort = 7686 diff --git a/candidate_relay_test.go b/candidate_relay_test.go index e4a7352..5a98a5b 100644 --- a/candidate_relay_test.go +++ b/candidate_relay_test.go @@ -24,8 +24,7 @@ func optimisticAuthHandler(string, string, net.Addr) (key []byte, ok bool) { func TestRelayOnlyConnection(t *testing.T) { // Limit runtime in case of deadlocks - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() defer test.CheckRoutines(t)() diff --git a/candidate_server_reflexive_test.go b/candidate_server_reflexive_test.go index 3f4361d..3d8244a 100644 --- a/candidate_server_reflexive_test.go +++ b/candidate_server_reflexive_test.go @@ -22,8 +22,7 @@ func TestServerReflexiveOnlyConnection(t *testing.T) { defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() serverPort := randomPort(t) serverListener, err := net.ListenPacket("udp4", "127.0.0.1:"+strconv.Itoa(serverPort)) diff --git a/connectivity_vnet_test.go b/connectivity_vnet_test.go index 2aa48f4..f85ac54 100644 --- a/connectivity_vnet_test.go +++ b/connectivity_vnet_test.go @@ -450,8 +450,7 @@ func TestConnectivityVNet(t *testing.T) { func TestDisconnectedToConnected(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 10) - defer lim.Stop() + defer test.TimeOut(time.Second * 10).Stop() loggerFactory := logging.NewDefaultLoggerFactory() @@ -547,8 +546,7 @@ func TestDisconnectedToConnected(t *testing.T) { func TestWriteUseValidPair(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 10) - defer lim.Stop() + defer test.TimeOut(time.Second * 10).Stop() loggerFactory := logging.NewDefaultLoggerFactory() diff --git a/gather_test.go b/gather_test.go index 554d515..1c14fe7 100644 --- a/gather_test.go +++ b/gather_test.go @@ -92,8 +92,7 @@ func TestListenUDP(t *testing.T) { func TestGatherConcurrency(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() a, err := NewAgent(&AgentConfig{ NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}, @@ -119,8 +118,7 @@ func TestGatherConcurrency(t *testing.T) { func TestLoopbackCandidate(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() type testCase struct { name string agentConfig *AgentConfig @@ -229,8 +227,7 @@ func TestLoopbackCandidate(t *testing.T) { func TestSTUNConcurrency(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() serverPort := randomPort(t) serverListener, err := net.ListenPacket("udp4", localhostIPStr+":"+strconv.Itoa(serverPort)) @@ -304,8 +301,7 @@ func TestSTUNConcurrency(t *testing.T) { func TestTURNConcurrency(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() runTest := func(protocol stun.ProtoType, scheme stun.SchemeType, packetConn net.PacketConn, listener net.Listener, serverPort int) { packetConnConfigs := []turn.PacketConnConfig{} @@ -421,8 +417,7 @@ func TestTURNConcurrency(t *testing.T) { func TestSTUNTURNConcurrency(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 8) - defer lim.Stop() + defer test.TimeOut(time.Second * 8).Stop() serverPort := randomPort(t) serverListener, err := net.ListenPacket("udp4", localhostIPStr+":"+strconv.Itoa(serverPort)) @@ -465,7 +460,7 @@ func TestSTUNTURNConcurrency(t *testing.T) { require.NoError(t, err) { - gatherLim := test.TimeOut(time.Second * 3) // As TURN and STUN should be checked in parallel, this should complete before the default STUN timeout (5s) + defer test.TimeOut(time.Second * 3).Stop() // As TURN and STUN should be checked in parallel, this should complete before the default STUN timeout (5s) candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) require.NoError(t, a.OnCandidate(func(c Candidate) { if c != nil { @@ -475,8 +470,6 @@ func TestSTUNTURNConcurrency(t *testing.T) { require.NoError(t, a.GatherCandidates()) <-candidateGathered.Done() - - gatherLim.Stop() } require.NoError(t, a.Close()) @@ -492,8 +485,7 @@ func TestSTUNTURNConcurrency(t *testing.T) { func TestTURNSrflx(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() serverPort := randomPort(t) serverListener, err := net.ListenPacket("udp4", localhostIPStr+":"+strconv.Itoa(serverPort)) @@ -577,8 +569,7 @@ func (m *mockProxy) Dial(string, string) (net.Conn, error) { func TestTURNProxyDialer(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() proxyWasDialed, proxyWasDialedFunc := context.WithCancel(context.Background()) proxy.RegisterDialerType("tcp", func(*url.URL, proxy.Dialer) (proxy.Dialer, error) { @@ -627,8 +618,7 @@ func TestTURNProxyDialer(t *testing.T) { func TestUDPMuxDefaultWithNAT1To1IPsUsage(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() conn, err := net.ListenPacket("udp4", ":0") require.NoError(t, err) @@ -670,8 +660,7 @@ func TestUDPMuxDefaultWithNAT1To1IPsUsage(t *testing.T) { func TestMultiUDPMuxUsage(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() var expectedPorts []int var udpMuxInstances []UDPMux @@ -726,8 +715,7 @@ func TestMultiUDPMuxUsage(t *testing.T) { func TestMultiTCPMuxUsage(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() var expectedPorts []int var tcpMuxInstances []TCPMux @@ -785,8 +773,7 @@ func TestMultiTCPMuxUsage(t *testing.T) { func TestUniversalUDPMuxUsage(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IP{127, 0, 0, 1}, Port: randomPort(t)}) require.NoError(t, err) diff --git a/mdns_test.go b/mdns_test.go index 48eeb59..8c9a5d7 100644 --- a/mdns_test.go +++ b/mdns_test.go @@ -20,8 +20,7 @@ func TestMulticastDNSOnlyConnection(t *testing.T) { defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() cfg := &AgentConfig{ NetworkTypes: []NetworkType{NetworkTypeUDP4}, @@ -61,8 +60,7 @@ func TestMulticastDNSMixedConnection(t *testing.T) { defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() aAgent, err := NewAgent(&AgentConfig{ NetworkTypes: []NetworkType{NetworkTypeUDP4}, @@ -103,8 +101,7 @@ func TestMulticastDNSMixedConnection(t *testing.T) { func TestMulticastDNSStaticHostName(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() _, err := NewAgent(&AgentConfig{ NetworkTypes: []NetworkType{NetworkTypeUDP4}, diff --git a/transport_test.go b/transport_test.go index 80a6bef..ef79773 100644 --- a/transport_test.go +++ b/transport_test.go @@ -22,8 +22,7 @@ func TestStressDuplex(t *testing.T) { defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks - lim := test.TimeOut(time.Second * 20) - defer lim.Stop() + defer test.TimeOut(time.Second * 20).Stop() // Run the test stressDuplex(t) @@ -78,8 +77,7 @@ func TestTimeout(t *testing.T) { defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks - lim := test.TimeOut(time.Second * 20) - defer lim.Stop() + defer test.TimeOut(time.Second * 20).Stop() t.Run("WithoutDisconnectTimeout", func(t *testing.T) { ca, cb := pipe(nil) @@ -109,8 +107,7 @@ func TestReadClosed(t *testing.T) { defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks - lim := test.TimeOut(time.Second * 20) - defer lim.Stop() + defer test.TimeOut(time.Second * 20).Stop() ca, cb := pipe(nil) @@ -321,8 +318,7 @@ func TestConnStats(t *testing.T) { defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks - lim := test.TimeOut(time.Second * 20) - defer lim.Stop() + defer test.TimeOut(time.Second * 20).Stop() ca, cb := pipe(nil) if _, err := ca.Write(make([]byte, 10)); err != nil { diff --git a/transport_vnet_test.go b/transport_vnet_test.go index ff27ec6..36a22f5 100644 --- a/transport_vnet_test.go +++ b/transport_vnet_test.go @@ -23,8 +23,7 @@ func TestRemoteLocalAddr(t *testing.T) { defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks - lim := test.TimeOut(time.Second * 20) - defer lim.Stop() + defer test.TimeOut(time.Second * 20).Stop() // Agent0 is behind 1:1 NAT natType0 := &vnet.NATType{Mode: vnet.NATModeNAT1To1} diff --git a/udp_mux_multi_test.go b/udp_mux_multi_test.go index 2f250f9..bb12022 100644 --- a/udp_mux_multi_test.go +++ b/udp_mux_multi_test.go @@ -20,8 +20,7 @@ import ( func TestMultiUDPMux(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() conn1, err := net.ListenUDP(udp, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}) require.NoError(t, err) @@ -112,8 +111,7 @@ func testMultiUDPMuxConnections(t *testing.T, udpMuxMulti *MultiUDPMuxDefault, u func TestUnspecifiedUDPMux(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() muxPort := 7778 udpMuxMulti, err := NewMultiUDPMuxFromPort(muxPort, UDPMuxFromPortWithInterfaceFilter(func(s string) bool { diff --git a/udp_mux_test.go b/udp_mux_test.go index 253d38e..5f8b1e0 100644 --- a/udp_mux_test.go +++ b/udp_mux_test.go @@ -23,8 +23,7 @@ import ( func TestUDPMux(t *testing.T) { defer test.CheckRoutines(t)() - lim := test.TimeOut(time.Second * 30) - defer lim.Stop() + defer test.TimeOut(time.Second * 30).Stop() conn4, err := net.ListenUDP(udp, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}) require.NoError(t, err) From b2e40ad7fa70503f4f3ee499629471342336e84e Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Sat, 23 Mar 2024 20:34:30 -0400 Subject: [PATCH 018/114] Partial undo of 85a3a7f52407 Broke i386 tests --- gather_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gather_test.go b/gather_test.go index 1c14fe7..5fd42b6 100644 --- a/gather_test.go +++ b/gather_test.go @@ -460,7 +460,7 @@ func TestSTUNTURNConcurrency(t *testing.T) { require.NoError(t, err) { - defer test.TimeOut(time.Second * 3).Stop() // As TURN and STUN should be checked in parallel, this should complete before the default STUN timeout (5s) + gatherLim := test.TimeOut(time.Second * 3) // As TURN and STUN should be checked in parallel, this should complete before the default STUN timeout (5s) candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) require.NoError(t, a.OnCandidate(func(c Candidate) { if c != nil { @@ -470,6 +470,7 @@ func TestSTUNTURNConcurrency(t *testing.T) { require.NoError(t, a.GatherCandidates()) <-candidateGathered.Done() + gatherLim.Stop() } require.NoError(t, a.Close()) From 52f2075c2e91a4912adcf4a7a13cdf019fbd5bd6 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Sat, 23 Mar 2024 21:01:58 -0400 Subject: [PATCH 019/114] Replace t.(Error/Fatal) with require.NoError Make tests easier to read --- active_tcp_test.go | 16 +++------- agent_test.go | 48 ++++++++---------------------- candidate_relay_test.go | 16 +++------- candidate_server_reflexive_test.go | 16 +++------- gather_test.go | 5 ++-- gather_vnet_test.go | 33 ++++++++++---------- mdns_test.go | 36 ++++++---------------- rand_test.go | 10 +++---- transport_test.go | 16 +++------- 9 files changed, 58 insertions(+), 138 deletions(-) diff --git a/active_tcp_test.go b/active_tcp_test.go index 3df2c5e..7b696d1 100644 --- a/active_tcp_test.go +++ b/active_tcp_test.go @@ -170,14 +170,10 @@ func TestActiveTCP_NonBlocking(t *testing.T) { } aAgent, err := NewAgent(cfg) - if err != nil { - t.Error(err) - } + require.NoError(t, err) bAgent, err := NewAgent(cfg) - if err != nil { - t.Error(err) - } + require.NoError(t, err) isConnected := make(chan interface{}) err = aAgent.OnConnectionStateChange(func(c ConnectionState) { @@ -185,15 +181,11 @@ func TestActiveTCP_NonBlocking(t *testing.T) { close(isConnected) } }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // Add a invalid ice-tcp candidate to each invalidCandidate, err := UnmarshalCandidate("1052353102 1 tcp 1675624447 192.0.2.1 8080 typ host tcptype passive") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) require.NoError(t, aAgent.AddRemoteCandidate(invalidCandidate)) require.NoError(t, bAgent.AddRemoteCandidate(invalidCandidate)) diff --git a/agent_test.go b/agent_test.go index 0207476..bbb44cf 100644 --- a/agent_test.go +++ b/agent_test.go @@ -68,9 +68,7 @@ func TestHandlePeerReflexive(t *testing.T) { stun.NewShortTermIntegrity(a.localPwd), stun.Fingerprint, ) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // nolint: contextcheck a.handleInbound(msg, local, remote) @@ -163,9 +161,7 @@ func TestHandlePeerReflexive(t *testing.T) { stun.NewShortTermIntegrity(a.remotePwd), stun.Fingerprint, ) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // nolint: contextcheck a.handleInbound(msg, local, remote) @@ -353,9 +349,7 @@ func TestInboundValidity(t *testing.T) { stun.NewShortTermIntegrity(key), stun.Fingerprint, ) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) return msg } @@ -449,9 +443,7 @@ func TestInboundValidity(t *testing.T) { stun.NewUsername(a.localUfrag+":"+a.remoteUfrag), stun.NewShortTermIntegrity(a.localPwd), ) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // nolint: contextcheck a.handleInbound(msg, local, remote) @@ -547,14 +539,10 @@ func TestConnectionStateCallback(t *testing.T) { } aAgent, err := NewAgent(cfg) - if err != nil { - t.Error(err) - } + require.NoError(t, err) bAgent, err := NewAgent(cfg) - if err != nil { - t.Error(err) - } + require.NoError(t, err) isChecking := make(chan interface{}) isConnected := make(chan interface{}) @@ -576,9 +564,7 @@ func TestConnectionStateCallback(t *testing.T) { default: } }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) connect(aAgent, bAgent) @@ -1385,14 +1371,10 @@ func TestCloseInConnectionStateCallback(t *testing.T) { } aAgent, err := NewAgent(cfg) - if err != nil { - t.Error(err) - } + require.NoError(t, err) bAgent, err := NewAgent(cfg) - if err != nil { - t.Error(err) - } + require.NoError(t, err) isClosed := make(chan interface{}) isConnected := make(chan interface{}) @@ -1406,9 +1388,7 @@ func TestCloseInConnectionStateCallback(t *testing.T) { default: } }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) connect(aAgent, bAgent) close(isConnected) @@ -1449,9 +1429,7 @@ func TestRunTaskInConnectionStateCallback(t *testing.T) { close(isComplete) } }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) connect(aAgent, bAgent) @@ -1713,9 +1691,7 @@ func TestAcceptAggressiveNomination(t *testing.T) { PriorityAttr(priority), stun.Fingerprint, ) - if err1 != nil { - t.Fatal(err1) - } + require.NoError(t, err1) return msg } diff --git a/candidate_relay_test.go b/candidate_relay_test.go index 5a98a5b..b3ebc26 100644 --- a/candidate_relay_test.go +++ b/candidate_relay_test.go @@ -60,24 +60,16 @@ func TestRelayOnlyConnection(t *testing.T) { } aAgent, err := NewAgent(cfg) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) aNotifier, aConnected := onConnected() - if err = aAgent.OnConnectionStateChange(aNotifier); err != nil { - t.Fatal(err) - } + require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) bAgent, err := NewAgent(cfg) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) bNotifier, bConnected := onConnected() - if err = bAgent.OnConnectionStateChange(bNotifier); err != nil { - t.Fatal(err) - } + require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) connect(aAgent, bAgent) <-aConnected diff --git a/candidate_server_reflexive_test.go b/candidate_server_reflexive_test.go index 3d8244a..037c058 100644 --- a/candidate_server_reflexive_test.go +++ b/candidate_server_reflexive_test.go @@ -53,24 +53,16 @@ func TestServerReflexiveOnlyConnection(t *testing.T) { } aAgent, err := NewAgent(cfg) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) aNotifier, aConnected := onConnected() - if err = aAgent.OnConnectionStateChange(aNotifier); err != nil { - t.Fatal(err) - } + require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) bAgent, err := NewAgent(cfg) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) bNotifier, bConnected := onConnected() - if err = bAgent.OnConnectionStateChange(bNotifier); err != nil { - t.Fatal(err) - } + require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) connect(aAgent, bAgent) <-aConnected diff --git a/gather_test.go b/gather_test.go index 5fd42b6..54c8a8d 100644 --- a/gather_test.go +++ b/gather_test.go @@ -66,9 +66,8 @@ func TestListenUDP(t *testing.T) { require.NotNil(t, conn, "listenUDP error with no port restriction return a nil conn") _, port, err = net.SplitHostPort(conn.LocalAddr().String()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) + p, _ := strconv.Atoi(port) if p < portMin || p > portMax { t.Fatalf("listenUDP with port restriction [%d, %d] listened on incorrect port (%s)", portMin, portMax, port) diff --git a/gather_vnet_test.go b/gather_vnet_test.go index 30ee32f..ce11e83 100644 --- a/gather_vnet_test.go +++ b/gather_vnet_test.go @@ -37,9 +37,8 @@ func TestVNetGather(t *testing.T) { localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) if len(localIPs) > 0 { t.Fatal("should return no local IP") - } else if err != nil { - t.Fatal(err) } + require.NoError(t, err) require.NoError(t, a.Close()) }) @@ -77,9 +76,8 @@ func TestVNetGather(t *testing.T) { localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) if len(localIPs) == 0 { t.Fatal("should have one local IP") - } else if err != nil { - t.Fatal(err) } + require.NoError(t, err) for _, ip := range localIPs { if ip.IsLoopback() { @@ -120,9 +118,8 @@ func TestVNetGather(t *testing.T) { localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) if len(localIPs) == 0 { t.Fatal("localInterfaces found no interfaces, unable to test") - } else if err != nil { - t.Fatal(err) } + require.NoError(t, err) ip := localIPs[0] @@ -150,9 +147,9 @@ func TestVNetGather(t *testing.T) { } _, port, err := net.SplitHostPort(conn.LocalAddr().String()) - if err != nil { - t.Fatal(err) - } else if port != "5000" { + + require.NoError(t, err) + if port != "5000" { t.Fatalf("listenUDP with port restriction of 5000 listened on incorrect port (%s)", port) } @@ -389,9 +386,9 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { require.NoError(t, err) localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) - if err != nil { - t.Fatal(err) - } else if len(localIPs) != 0 { + require.NoError(t, err) + + if len(localIPs) != 0 { t.Fatal("InterfaceFilter should have excluded everything") } @@ -409,9 +406,9 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { require.NoError(t, err) localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) - if err != nil { - t.Fatal(err) - } else if len(localIPs) != 0 { + require.NoError(t, err) + + if len(localIPs) != 0 { t.Fatal("IPFilter should have excluded everything") } @@ -429,9 +426,9 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { require.NoError(t, err) localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) - if err != nil { - t.Fatal(err) - } else if len(localIPs) == 0 { + require.NoError(t, err) + + if len(localIPs) == 0 { t.Fatal("InterfaceFilter should not have excluded anything") } diff --git a/mdns_test.go b/mdns_test.go index 8c9a5d7..a82d6b1 100644 --- a/mdns_test.go +++ b/mdns_test.go @@ -29,24 +29,16 @@ func TestMulticastDNSOnlyConnection(t *testing.T) { } aAgent, err := NewAgent(cfg) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) aNotifier, aConnected := onConnected() - if err = aAgent.OnConnectionStateChange(aNotifier); err != nil { - t.Fatal(err) - } + require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) bAgent, err := NewAgent(cfg) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) bNotifier, bConnected := onConnected() - if err = bAgent.OnConnectionStateChange(bNotifier); err != nil { - t.Fatal(err) - } + require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) connect(aAgent, bAgent) <-aConnected @@ -67,28 +59,20 @@ func TestMulticastDNSMixedConnection(t *testing.T) { CandidateTypes: []CandidateType{CandidateTypeHost}, MulticastDNSMode: MulticastDNSModeQueryAndGather, }) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) aNotifier, aConnected := onConnected() - if err = aAgent.OnConnectionStateChange(aNotifier); err != nil { - t.Fatal(err) - } + require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) bAgent, err := NewAgent(&AgentConfig{ NetworkTypes: []NetworkType{NetworkTypeUDP4}, CandidateTypes: []CandidateType{CandidateTypeHost}, MulticastDNSMode: MulticastDNSModeQueryOnly, }) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) bNotifier, bConnected := onConnected() - if err = bAgent.OnConnectionStateChange(bNotifier); err != nil { - t.Fatal(err) - } + require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) connect(aAgent, bAgent) <-aConnected @@ -133,9 +117,7 @@ func TestMulticastDNSStaticHostName(t *testing.T) { func TestGenerateMulticastDNSName(t *testing.T) { name, err := generateMulticastDNSName() - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) isMDNSName := regexp.MustCompile( `^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}.local+$`, ).MatchString diff --git a/rand_test.go b/rand_test.go index 4cb12b6..ecc7053 100644 --- a/rand_test.go +++ b/rand_test.go @@ -6,6 +6,8 @@ package ice import ( "sync" "testing" + + "github.com/stretchr/testify/require" ) func TestRandomGeneratorCollision(t *testing.T) { @@ -22,18 +24,14 @@ func TestRandomGeneratorCollision(t *testing.T) { "PWD": { gen: func(t *testing.T) string { s, err := generatePwd() - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) return s }, }, "Ufrag": { gen: func(t *testing.T) string { s, err := generateUFrag() - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) return s }, }, diff --git a/transport_test.go b/transport_test.go index ef79773..216ee8b 100644 --- a/transport_test.go +++ b/transport_test.go @@ -15,6 +15,7 @@ import ( "github.com/pion/stun/v2" "github.com/pion/transport/v3/test" + "github.com/stretchr/testify/require" ) func TestStressDuplex(t *testing.T) { @@ -134,14 +135,8 @@ func stressDuplex(t *testing.T) { ca, cb := pipe(nil) defer func() { - err := ca.Close() - if err != nil { - t.Fatal(err) - } - err = cb.Close() - if err != nil { - t.Fatal(err) - } + require.NoError(t, ca.Close()) + require.NoError(t, cb.Close()) }() opt := test.Options{ @@ -149,10 +144,7 @@ func stressDuplex(t *testing.T) { MsgCount: 1, // Order not reliable due to UDP & potentially multiple candidate pairs. } - err := test.StressDuplex(ca, cb, opt) - if err != nil { - t.Fatal(err) - } + require.NoError(t, test.StressDuplex(ca, cb, opt)) } func check(err error) { From 66051b6877eadc8ddeaf68a25eb8a68800e65553 Mon Sep 17 00:00:00 2001 From: sebapeti Date: Mon, 25 Mar 2024 15:16:16 +0100 Subject: [PATCH 020/114] Improve performance of UDPMux map lookups UDPMux is using a map to lookup addresses of each packets. Unfortunately the key is based on a string and each time we want to check the map, a conversion of the UDP address to string is made (.String()) which is expensive. This CR replace the string key by a binary key called ipPort. This structure contains a netip.Addr field and ipPort could be used as a map key --- errors.go | 2 ++ udp_mux.go | 52 ++++++++++++++++++++++++++++++++++++----------- udp_muxed_conn.go | 28 ++++++++++++++++--------- 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/errors.go b/errors.go index 46785ed..e39c7cf 100644 --- a/errors.go +++ b/errors.go @@ -133,6 +133,8 @@ var ( errWriteSTUNMessage = errors.New("failed to send STUN message") errWriteSTUNMessageToIceConn = errors.New("failed to write STUN message to ICE connection") errXORMappedAddrTimeout = errors.New("timeout while waiting for XORMappedAddr") + errFailedToCastUDPAddr = errors.New("failed to cast net.Addr to net.UDPAddr") + errInvalidIPAddress = errors.New("invalid ip address") // UDPMuxDefault should not listen on unspecified address, but to keep backward compatibility, don't return error now. // will be used in the future. diff --git a/udp_mux.go b/udp_mux.go index cf01537..dc45458 100644 --- a/udp_mux.go +++ b/udp_mux.go @@ -7,6 +7,7 @@ import ( "errors" "io" "net" + "net/netip" "os" "strings" "sync" @@ -36,7 +37,7 @@ type UDPMuxDefault struct { connsIPv4, connsIPv6 map[string]*udpMuxedConn addressMapMu sync.RWMutex - addressMap map[string]*udpMuxedConn + addressMap map[ipPort]*udpMuxedConn // Buffer pool to recycle buffers for net.UDPAddr encodes/decodes pool *sync.Pool @@ -51,8 +52,9 @@ const maxAddrSize = 512 // UDPMuxParams are parameters for UDPMux. type UDPMuxParams struct { - Logger logging.LeveledLogger - UDPConn net.PacketConn + Logger logging.LeveledLogger + UDPConn net.PacketConn + UDPConnString string // Required for gathering local addresses // in case a un UDPConn is passed which does not @@ -103,9 +105,10 @@ func NewUDPMuxDefault(params UDPMuxParams) *UDPMuxDefault { } } } + params.UDPConnString = params.UDPConn.LocalAddr().String() m := &UDPMuxDefault{ - addressMap: map[string]*udpMuxedConn{}, + addressMap: map[ipPort]*udpMuxedConn{}, params: params, connsIPv4: make(map[string]*udpMuxedConn), connsIPv6: make(map[string]*udpMuxedConn), @@ -142,7 +145,7 @@ func (m *UDPMuxDefault) GetListenAddresses() []net.Addr { // creates the connection if an existing one can't be found func (m *UDPMuxDefault) GetConn(ufrag string, addr net.Addr) (net.PacketConn, error) { // don't check addr for mux using unspecified address - if len(m.localAddrsForUnspecified) == 0 && m.params.UDPConn.LocalAddr().String() != addr.String() { + if len(m.localAddrsForUnspecified) == 0 && m.params.UDPConnString != addr.String() { return nil, errInvalidAddress } @@ -246,7 +249,7 @@ func (m *UDPMuxDefault) writeTo(buf []byte, rAddr net.Addr) (n int, err error) { return m.params.UDPConn.WriteTo(buf, rAddr) } -func (m *UDPMuxDefault) registerConnForAddress(conn *udpMuxedConn, addr string) { +func (m *UDPMuxDefault) registerConnForAddress(conn *udpMuxedConn, addr ipPort) { if m.IsClosed() { return } @@ -260,7 +263,7 @@ func (m *UDPMuxDefault) registerConnForAddress(conn *udpMuxedConn, addr string) } m.addressMap[addr] = conn - m.params.Logger.Debugf("Registered %s for %s", addr, conn.params.Key) + m.params.Logger.Debugf("Registered %s for %s", addr.addr.String(), conn.params.Key) } func (m *UDPMuxDefault) createMuxedConn(key string) *udpMuxedConn { @@ -296,15 +299,20 @@ func (m *UDPMuxDefault) connWorker() { return } - udpAddr, ok := addr.(*net.UDPAddr) + netUDPAddr, ok := addr.(*net.UDPAddr) if !ok { logger.Errorf("Underlying PacketConn did not return a UDPAddr") return } + udpAddr, err := newIPPort(netUDPAddr.IP, uint16(netUDPAddr.Port)) + if err != nil { + logger.Errorf("Failed to create a new IP/Port host pair") + return + } // If we have already seen this address dispatch to the appropriate destination m.addressMapMu.Lock() - destinationConn := m.addressMap[addr.String()] + destinationConn := m.addressMap[udpAddr] m.addressMapMu.Unlock() // If we haven't seen this address before but is a STUN packet lookup by ufrag @@ -325,7 +333,7 @@ func (m *UDPMuxDefault) connWorker() { } ufrag := strings.Split(string(attr), ":")[0] - isIPv6 := udpAddr.IP.To4() == nil + isIPv6 := netUDPAddr.IP.To4() == nil m.mu.Lock() destinationConn, _ = m.getConn(ufrag, isIPv6) @@ -333,11 +341,11 @@ func (m *UDPMuxDefault) connWorker() { } if destinationConn == nil { - m.params.Logger.Tracef("Dropping packet from %s, addr: %s", udpAddr.String(), addr.String()) + m.params.Logger.Tracef("Dropping packet from %s, addr: %s", udpAddr.addr.String(), addr.String()) continue } - if err = destinationConn.writePacket(buf[:n], udpAddr); err != nil { + if err = destinationConn.writePacket(buf[:n], netUDPAddr); err != nil { m.params.Logger.Errorf("Failed to write packet: %v", err) } } @@ -361,3 +369,23 @@ func newBufferHolder(size int) *bufferHolder { buf: make([]byte, size), } } + +type ipPort struct { + addr netip.Addr + port uint16 +} + +// newIPPort create a custom type of address based on netip.Addr and +// port. The underlying ip address passed is converted to IPv6 format +// to simplify ip address handling +func newIPPort(ip net.IP, port uint16) (ipPort, error) { + n, ok := netip.AddrFromSlice(ip.To16()) + if !ok { + return ipPort{}, errInvalidIPAddress + } + + return ipPort{ + addr: n, + port: port, + }, nil +} diff --git a/udp_muxed_conn.go b/udp_muxed_conn.go index e69c307..fb05e23 100644 --- a/udp_muxed_conn.go +++ b/udp_muxed_conn.go @@ -26,7 +26,7 @@ type udpMuxedConnParams struct { type udpMuxedConn struct { params *udpMuxedConnParams // Remote addresses that we have sent to on this conn - addresses []string + addresses []ipPort // Channel holding incoming packets buf *packetio.Buffer @@ -81,9 +81,17 @@ func (c *udpMuxedConn) WriteTo(buf []byte, rAddr net.Addr) (n int, err error) { return 0, io.ErrClosedPipe } // Each time we write to a new address, we'll register it with the mux - addr := rAddr.String() - if !c.containsAddress(addr) { - c.addAddress(addr) + netUDPAddr, ok := rAddr.(*net.UDPAddr) + if !ok { + return 0, errFailedToCastUDPAddr + } + + ipAndPort, err := newIPPort(netUDPAddr.IP, uint16(netUDPAddr.Port)) + if err != nil { + return 0, err + } + if !c.containsAddress(ipAndPort) { + c.addAddress(ipAndPort) } return c.params.Mux.writeTo(buf, rAddr) @@ -127,15 +135,15 @@ func (c *udpMuxedConn) isClosed() bool { } } -func (c *udpMuxedConn) getAddresses() []string { +func (c *udpMuxedConn) getAddresses() []ipPort { c.mu.Lock() defer c.mu.Unlock() - addresses := make([]string, len(c.addresses)) + addresses := make([]ipPort, len(c.addresses)) copy(addresses, c.addresses) return addresses } -func (c *udpMuxedConn) addAddress(addr string) { +func (c *udpMuxedConn) addAddress(addr ipPort) { c.mu.Lock() c.addresses = append(c.addresses, addr) c.mu.Unlock() @@ -144,11 +152,11 @@ func (c *udpMuxedConn) addAddress(addr string) { c.params.Mux.registerConnForAddress(c, addr) } -func (c *udpMuxedConn) removeAddress(addr string) { +func (c *udpMuxedConn) removeAddress(addr ipPort) { c.mu.Lock() defer c.mu.Unlock() - newAddresses := make([]string, 0, len(c.addresses)) + newAddresses := make([]ipPort, 0, len(c.addresses)) for _, a := range c.addresses { if a != addr { newAddresses = append(newAddresses, a) @@ -158,7 +166,7 @@ func (c *udpMuxedConn) removeAddress(addr string) { c.addresses = newAddresses } -func (c *udpMuxedConn) containsAddress(addr string) bool { +func (c *udpMuxedConn) containsAddress(addr ipPort) bool { c.mu.Lock() defer c.mu.Unlock() for _, a := range c.addresses { From ae1ba6fcbbf10b045ce4122603b69fe27f171fa6 Mon Sep 17 00:00:00 2001 From: Pion <59523206+pionbot@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:57:45 +0000 Subject: [PATCH 021/114] Update CI configs to v0.11.4 Update lint scripts and CI configs. --- .github/workflows/api.yaml | 20 ++++++++++++++++++++ .github/workflows/release.yml | 2 +- .github/workflows/test.yaml | 6 +++--- .github/workflows/tidy-check.yaml | 2 +- 4 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/api.yaml diff --git a/.github/workflows/api.yaml b/.github/workflows/api.yaml new file mode 100644 index 0000000..1032179 --- /dev/null +++ b/.github/workflows/api.yaml @@ -0,0 +1,20 @@ +# +# DO NOT EDIT THIS FILE +# +# It is automatically copied from https://github.com/pion/.goassets repository. +# If this repository should have package specific CI config, +# remove the repository name from .goassets/.github/workflows/assets-sync.yml. +# +# If you want to update the shared CI config, send a PR to +# https://github.com/pion/.goassets instead of this repository. +# +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + +name: API +on: + pull_request: + +jobs: + check: + uses: pion/.goassets/.github/workflows/api.reusable.yml@master diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 01227e2..0e72ea4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,4 +21,4 @@ jobs: release: uses: pion/.goassets/.github/workflows/release.reusable.yml@master with: - go-version: '1.20' # auto-update/latest-go-version + go-version: "1.22" # auto-update/latest-go-version diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index c8294ef..ad6eb90 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -23,7 +23,7 @@ jobs: uses: pion/.goassets/.github/workflows/test.reusable.yml@master strategy: matrix: - go: ['1.21', '1.20'] # auto-update/supported-go-version-list + go: ["1.22", "1.21"] # auto-update/supported-go-version-list fail-fast: false with: go-version: ${{ matrix.go }} @@ -32,7 +32,7 @@ jobs: uses: pion/.goassets/.github/workflows/test-i386.reusable.yml@master strategy: matrix: - go: ['1.21', '1.20'] # auto-update/supported-go-version-list + go: ["1.22", "1.21"] # auto-update/supported-go-version-list fail-fast: false with: go-version: ${{ matrix.go }} @@ -40,4 +40,4 @@ jobs: test-wasm: uses: pion/.goassets/.github/workflows/test-wasm.reusable.yml@master with: - go-version: '1.20' # auto-update/latest-go-version + go-version: "1.22" # auto-update/latest-go-version diff --git a/.github/workflows/tidy-check.yaml b/.github/workflows/tidy-check.yaml index 33d6b50..417e730 100644 --- a/.github/workflows/tidy-check.yaml +++ b/.github/workflows/tidy-check.yaml @@ -22,4 +22,4 @@ jobs: tidy: uses: pion/.goassets/.github/workflows/tidy-check.reusable.yml@master with: - go-version: '1.21' # auto-update/latest-go-version + go-version: "1.22" # auto-update/latest-go-version From 39c0392295a06b5cac940f7a49d61cc74ca56c6f Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Wed, 27 Mar 2024 11:24:02 -0400 Subject: [PATCH 022/114] Support IPv6 from mDNS --- active_tcp.go | 14 ++- active_tcp_test.go | 39 ++++--- addr.go | 116 +++++++++++++++++---- agent.go | 69 ++++++++----- agent_test.go | 1 + candidate.go | 1 + candidate_base.go | 17 +++- candidate_host.go | 34 ++++--- candidate_peer_reflexive.go | 14 +-- candidate_relay.go | 25 +++-- candidate_server_reflexive.go | 29 ++++-- candidate_test.go | 159 +++++++++++++++-------------- gather.go | 108 ++++++++++++++++---- gather_test.go | 6 +- gather_vnet_test.go | 24 ++--- go.mod | 4 +- go.sum | 7 +- mdns.go | 84 +++++++++++++-- mdns_test.go | 187 +++++++++++++++++++++++----------- net.go | 82 +++++++++------ net_test.go | 62 +++++++---- networktype.go | 12 +-- networktype_test.go | 4 +- tcp_mux.go | 23 +++-- tcp_mux_multi.go | 4 +- tcp_mux_test.go | 17 +++- transport_test.go | 8 ++ udp_mux.go | 24 +++-- udp_mux_multi.go | 12 ++- udp_mux_multi_test.go | 7 +- udp_mux_test.go | 22 +++- udp_muxed_conn.go | 2 +- 32 files changed, 829 insertions(+), 388 deletions(-) diff --git a/active_tcp.go b/active_tcp.go index 4ffcb6e..a6f8387 100644 --- a/active_tcp.go +++ b/active_tcp.go @@ -7,6 +7,7 @@ import ( "context" "io" "net" + "net/netip" "sync/atomic" "time" @@ -20,7 +21,7 @@ type activeTCPConn struct { closed int32 } -func newActiveTCPConn(ctx context.Context, localAddress, remoteAddress string, log logging.LeveledLogger) (a *activeTCPConn) { +func newActiveTCPConn(ctx context.Context, localAddress string, remoteAddress netip.AddrPort, log logging.LeveledLogger) (a *activeTCPConn) { a = &activeTCPConn{ readBuffer: packetio.NewBuffer(), writeBuffer: packetio.NewBuffer(), @@ -42,12 +43,11 @@ func newActiveTCPConn(ctx context.Context, localAddress, remoteAddress string, l dialer := &net.Dialer{ LocalAddr: laddr, } - conn, err := dialer.DialContext(ctx, "tcp", remoteAddress) + conn, err := dialer.DialContext(ctx, "tcp", remoteAddress.String()) if err != nil { log.Infof("Failed to dial TCP address %s: %v", remoteAddress, err) return } - a.remoteAddr.Store(conn.RemoteAddr()) go func() { @@ -95,8 +95,9 @@ func (a *activeTCPConn) ReadFrom(buff []byte) (n int, srcAddr net.Addr, err erro return 0, nil, io.ErrClosedPipe } - srcAddr = a.RemoteAddr() n, err = a.readBuffer.Read(buff) + // RemoteAddr is assuredly set *after* we can read from the buffer + srcAddr = a.RemoteAddr() return } @@ -123,6 +124,11 @@ func (a *activeTCPConn) LocalAddr() net.Addr { return &net.TCPAddr{} } +// RemoteAddr returns the remote address of the connection which is only +// set once a background goroutine has successfully dialed. That means +// this may return ":0" for the address prior to that happening. If this +// becomes an issue, we can introduce a synchronization point between Dial +// and these methods. func (a *activeTCPConn) RemoteAddr() net.Addr { if v, ok := a.remoteAddr.Load().(*net.TCPAddr); ok { return v diff --git a/active_tcp_test.go b/active_tcp_test.go index 7b696d1..44d6a47 100644 --- a/active_tcp_test.go +++ b/active_tcp_test.go @@ -8,6 +8,7 @@ package ice import ( "net" + "net/netip" "testing" "time" @@ -17,21 +18,21 @@ import ( "github.com/stretchr/testify/require" ) -func getLocalIPAddress(t *testing.T, networkType NetworkType) net.IP { +func getLocalIPAddress(t *testing.T, networkType NetworkType) netip.Addr { net, err := stdnet.NewNet() require.NoError(t, err) - localIPs, err := localInterfaces(net, nil, nil, []NetworkType{networkType}, false) + _, localAddrs, err := localInterfaces(net, problematicNetworkInterfaces, nil, []NetworkType{networkType}, false) require.NoError(t, err) - require.NotEmpty(t, localIPs) - return localIPs[0] + require.NotEmpty(t, localAddrs) + return localAddrs[0] } func ipv6Available(t *testing.T) bool { net, err := stdnet.NewNet() require.NoError(t, err) - localIPs, err := localInterfaces(net, nil, nil, []NetworkType{NetworkTypeTCP6}, false) + _, localAddrs, err := localInterfaces(net, problematicNetworkInterfaces, nil, []NetworkType{NetworkTypeTCP6}, false) require.NoError(t, err) - return len(localIPs) > 0 + return len(localAddrs) > 0 } func TestActiveTCP(t *testing.T) { @@ -43,8 +44,9 @@ func TestActiveTCP(t *testing.T) { type testCase struct { name string networkTypes []NetworkType - listenIPAddress net.IP + listenIPAddress netip.Addr selectedPairNetworkType string + useMDNS bool } testCases := []testCase{ @@ -69,12 +71,16 @@ func TestActiveTCP(t *testing.T) { networkTypes: []NetworkType{NetworkTypeTCP6}, listenIPAddress: getLocalIPAddress(t, NetworkTypeTCP6), selectedPairNetworkType: tcp, + // if we don't use mDNS, we will very liekly be filtering out location tracked ips. + useMDNS: true, }, testCase{ - name: "UDP is preferred over TCP6", // This fails some time + name: "UDP is preferred over TCP6", networkTypes: supportedNetworkTypes(), listenIPAddress: getLocalIPAddress(t, NetworkTypeTCP6), selectedPairNetworkType: udp, + // if we don't use mDNS, we will very liekly be filtering out location tracked ips. + useMDNS: true, }, ) } @@ -84,8 +90,9 @@ func TestActiveTCP(t *testing.T) { r := require.New(t) listener, err := net.ListenTCP("tcp", &net.TCPAddr{ - IP: testCase.listenIPAddress, + IP: testCase.listenIPAddress.AsSlice(), Port: listenPort, + Zone: testCase.listenIPAddress.Zone(), }) r.NoError(err) defer func() { @@ -107,14 +114,18 @@ func TestActiveTCP(t *testing.T) { r.NotNil(tcpMux.LocalAddr(), "tcpMux.LocalAddr() is nil") hostAcceptanceMinWait := 100 * time.Millisecond - passiveAgent, err := NewAgent(&AgentConfig{ + cfg := &AgentConfig{ TCPMux: tcpMux, CandidateTypes: []CandidateType{CandidateTypeHost}, NetworkTypes: testCase.networkTypes, LoggerFactory: loggerFactory, - IncludeLoopback: true, HostAcceptanceMinWait: &hostAcceptanceMinWait, - }) + InterfaceFilter: problematicNetworkInterfaces, + } + if testCase.useMDNS { + cfg.MulticastDNSMode = MulticastDNSModeQueryAndGather + } + passiveAgent, err := NewAgent(cfg) r.NoError(err) r.NotNil(passiveAgent) @@ -123,6 +134,7 @@ func TestActiveTCP(t *testing.T) { NetworkTypes: testCase.networkTypes, LoggerFactory: loggerFactory, HostAcceptanceMinWait: &hostAcceptanceMinWait, + InterfaceFilter: problematicNetworkInterfaces, }) r.NoError(err) r.NotNil(activeAgent) @@ -166,7 +178,8 @@ func TestActiveTCP_NonBlocking(t *testing.T) { defer test.TimeOut(time.Second * 5).Stop() cfg := &AgentConfig{ - NetworkTypes: supportedNetworkTypes(), + NetworkTypes: supportedNetworkTypes(), + InterfaceFilter: problematicNetworkInterfaces, } aAgent, err := NewAgent(cfg) diff --git a/addr.go b/addr.go index 1d70025..fb40061 100644 --- a/addr.go +++ b/addr.go @@ -4,52 +4,126 @@ package ice import ( + "fmt" "net" + "net/netip" ) -func parseMulticastAnswerAddr(in net.Addr) (net.IP, bool) { +func addrWithOptionalZone(addr netip.Addr, zone string) netip.Addr { + if zone == "" { + return addr + } + if addr.Is6() && (addr.IsLinkLocalUnicast() || addr.IsLinkLocalMulticast()) { + return addr.WithZone(zone) + } + return addr +} + +// parseAddrFromIface should only be used when it's known the address belongs to that interface. +// e.g. it's LocalAddress on a listener. +func parseAddrFromIface(in net.Addr, ifcName string) (netip.Addr, int, NetworkType, error) { + addr, port, nt, err := parseAddr(in) + if err != nil { + return netip.Addr{}, 0, 0, err + } + if _, ok := in.(*net.IPNet); ok { + // net.IPNet does not have a Zone but we provide it from the interface + addr = addrWithOptionalZone(addr, ifcName) + } + return addr, port, nt, nil +} + +func parseAddr(in net.Addr) (netip.Addr, int, NetworkType, error) { switch addr := in.(type) { + case *net.IPNet: + ipAddr, err := ipAddrToNetIP(addr.IP, "") + if err != nil { + return netip.Addr{}, 0, 0, err + } + return ipAddr, 0, 0, nil case *net.IPAddr: - return addr.IP, true + ipAddr, err := ipAddrToNetIP(addr.IP, addr.Zone) + if err != nil { + return netip.Addr{}, 0, 0, err + } + return ipAddr, 0, 0, nil case *net.UDPAddr: - return addr.IP, true + ipAddr, err := ipAddrToNetIP(addr.IP, addr.Zone) + if err != nil { + return netip.Addr{}, 0, 0, err + } + var nt NetworkType + if ipAddr.Is4() { + nt = NetworkTypeUDP4 + } else { + nt = NetworkTypeUDP6 + } + return ipAddr, addr.Port, nt, nil case *net.TCPAddr: - return addr.IP, true + ipAddr, err := ipAddrToNetIP(addr.IP, addr.Zone) + if err != nil { + return netip.Addr{}, 0, 0, err + } + var nt NetworkType + if ipAddr.Is4() { + nt = NetworkTypeTCP4 + } else { + nt = NetworkTypeTCP6 + } + return ipAddr, addr.Port, nt, nil + default: + return netip.Addr{}, 0, 0, addrParseError{in} } - return nil, false } -func parseAddr(in net.Addr) (net.IP, int, NetworkType, bool) { - switch addr := in.(type) { - case *net.UDPAddr: - return addr.IP, addr.Port, NetworkTypeUDP4, true - case *net.TCPAddr: - return addr.IP, addr.Port, NetworkTypeTCP4, true - } - return nil, 0, 0, false +type addrParseError struct { + addr net.Addr } -func createAddr(network NetworkType, ip net.IP, port int) net.Addr { +func (e addrParseError) Error() string { + return fmt.Sprintf("do not know how to parse address type %T", e.addr) +} + +type ipConvertError struct { + ip []byte +} + +func (e ipConvertError) Error() string { + return fmt.Sprintf("failed to convert IP '%s' to netip.Addr", e.ip) +} + +func ipAddrToNetIP(ip []byte, zone string) (netip.Addr, error) { + netIPAddr, ok := netip.AddrFromSlice(ip) + if !ok { + return netip.Addr{}, ipConvertError{ip} + } + // we'd rather have an IPv4-mapped IPv6 become IPv4 so that it is usable. + netIPAddr = netIPAddr.Unmap() + netIPAddr = addrWithOptionalZone(netIPAddr, zone) + return netIPAddr, nil +} + +func createAddr(network NetworkType, ip netip.Addr, port int) net.Addr { switch { case network.IsTCP(): - return &net.TCPAddr{IP: ip, Port: port} + return &net.TCPAddr{IP: ip.AsSlice(), Port: port, Zone: ip.Zone()} default: - return &net.UDPAddr{IP: ip, Port: port} + return &net.UDPAddr{IP: ip.AsSlice(), Port: port, Zone: ip.Zone()} } } func addrEqual(a, b net.Addr) bool { - aIP, aPort, aType, aOk := parseAddr(a) - if !aOk { + aIP, aPort, aType, aErr := parseAddr(a) + if aErr != nil { return false } - bIP, bPort, bType, bOk := parseAddr(b) - if !bOk { + bIP, bPort, bType, bErr := parseAddr(b) + if bErr != nil { return false } - return aType == bType && aIP.Equal(bIP) && aPort == bPort + return aType == bType && aIP.Compare(bIP) == 0 && aPort == bPort } // AddrPort is an IP and a port number. diff --git a/agent.go b/agent.go index 27d429d..1a8c897 100644 --- a/agent.go +++ b/agent.go @@ -9,7 +9,7 @@ import ( "context" "fmt" "net" - "strconv" + "net/netip" "strings" "sync" "sync/atomic" @@ -228,9 +228,22 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit } } + localIfcs, _, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, a.networkTypes, a.includeLoopback) + if err != nil { + return nil, fmt.Errorf("error getting local interfaces: %w", err) + } + // Opportunistic mDNS: If we can't open the connection, that's ok: we // can continue without it. - if a.mDNSConn, a.mDNSMode, err = createMulticastDNS(a.net, mDNSMode, mDNSName, log); err != nil { + if a.mDNSConn, a.mDNSMode, err = createMulticastDNS( + a.net, + a.networkTypes, + localIfcs, + a.includeLoopback, + mDNSMode, + mDNSName, + log, + ); err != nil { log.Warnf("Failed to initialize mDNS %s: %v", mDNSName, err) } @@ -592,19 +605,14 @@ func (a *Agent) resolveAndAddMulticastCandidate(c *CandidateHost) { if a.mDNSConn == nil { return } - _, src, err := a.mDNSConn.Query(c.context(), c.Address()) + + _, src, err := a.mDNSConn.QueryAddr(c.context(), c.Address()) if err != nil { a.log.Warnf("Failed to discover mDNS candidate %s: %v", c.Address(), err) return } - ip, ipOk := parseMulticastAnswerAddr(src) - if !ipOk { - a.log.Warnf("Failed to discover mDNS candidate %s: failed to parse IP", c.Address()) - return - } - - if err = c.setIP(ip); err != nil { + if err = c.setIPAddr(src); err != nil { a.log.Warnf("Failed to discover mDNS candidate %s: %v", c.Address(), err) return } @@ -626,17 +634,23 @@ func (a *Agent) requestConnectivityCheck() { } func (a *Agent) addRemotePassiveTCPCandidate(remoteCandidate Candidate) { - localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{remoteCandidate.NetworkType()}, a.includeLoopback) + _, localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{remoteCandidate.NetworkType()}, a.includeLoopback) if err != nil { a.log.Warnf("Failed to iterate local interfaces, host candidates will not be gathered %s", err) return } for i := range localIPs { + ip, _, _, err := parseAddr(remoteCandidate.addr()) + if err != nil { + a.log.Warnf("Failed to parse address: %s; error: %s", remoteCandidate.addr(), err) + continue + } + conn := newActiveTCPConn( a.loop, net.JoinHostPort(localIPs[i].String(), "0"), - net.JoinHostPort(remoteCandidate.Address(), strconv.Itoa(remoteCandidate.Port())), + netip.AddrPortFrom(ip, uint16(remoteCandidate.Port())), a.log, ) @@ -730,7 +744,9 @@ func (a *Agent) addCandidate(ctx context.Context, c Candidate, candidateConn net a.requestConnectivityCheck() - a.candidateNotifier.EnqueueCandidate(c) + if !c.filterForLocationTracking() { + a.candidateNotifier.EnqueueCandidate(c) + } }) } @@ -759,7 +775,12 @@ func (a *Agent) GetLocalCandidates() ([]Candidate, error) { err := a.loop.Run(a.loop, func(_ context.Context) { var candidates []Candidate for _, set := range a.localCandidates { - candidates = append(candidates, set...) + for _, c := range set { + if c.filterForLocationTracking() { + continue + } + candidates = append(candidates, c) + } } res = candidates }) @@ -841,9 +862,9 @@ func (a *Agent) deleteAllCandidates() { } func (a *Agent) findRemoteCandidate(networkType NetworkType, addr net.Addr) Candidate { - ip, port, _, ok := parseAddr(addr) - if !ok { - a.log.Warnf("Failed to parse address: %s", addr) + ip, port, _, err := parseAddr(addr) + if err != nil { + a.log.Warnf("Failed to parse address: %s; error: %s", addr, err) return nil } @@ -873,15 +894,15 @@ func (a *Agent) sendBindingRequest(m *stun.Message, local, remote Candidate) { func (a *Agent) sendBindingSuccess(m *stun.Message, local, remote Candidate) { base := remote - ip, port, _, ok := parseAddr(base.addr()) - if !ok { - a.log.Warnf("Failed to parse address: %s", base.addr()) + ip, port, _, err := parseAddr(base.addr()) + if err != nil { + a.log.Warnf("Failed to parse address: %s; error: %s", base.addr(), err) return } if out, err := stun.Build(m, stun.BindingSuccess, &stun.XORMappedAddress{ - IP: ip, + IP: ip.AsSlice(), Port: port, }, stun.NewShortTermIntegrity(a.localPwd), @@ -983,9 +1004,9 @@ func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr) } if remoteCandidate == nil { - ip, port, networkType, ok := parseAddr(remote) - if !ok { - a.log.Errorf("Failed to create parse remote net.Addr when creating remote prflx candidate") + ip, port, networkType, err := parseAddr(remote) + if err != nil { + a.log.Errorf("Failed to create parse remote net.Addr when creating remote prflx candidate: %s", err) return } diff --git a/agent_test.go b/agent_test.go index bbb44cf..552d421 100644 --- a/agent_test.go +++ b/agent_test.go @@ -536,6 +536,7 @@ func TestConnectionStateCallback(t *testing.T) { DisconnectedTimeout: &disconnectedDuration, FailedTimeout: &failedDuration, KeepaliveInterval: &KeepaliveInterval, + InterfaceFilter: problematicNetworkInterfaces, } aAgent, err := NewAgent(cfg) diff --git a/candidate.go b/candidate.go index 92a0076..4324159 100644 --- a/candidate.go +++ b/candidate.go @@ -61,6 +61,7 @@ type Candidate interface { Marshal() string addr() net.Addr + filterForLocationTracking() bool agent() *Agent context() context.Context diff --git a/candidate_base.go b/candidate_base.go index fd0f292..1a60899 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -43,6 +43,7 @@ type candidateBase struct { priorityOverride uint32 remoteCandidateCaches map[AddrPort]Candidate + isLocationTracked bool } // Done implements context.Context @@ -384,6 +385,14 @@ func (c *candidateBase) Priority() uint32 { // Equal is used to compare two candidateBases func (c *candidateBase) Equal(other Candidate) bool { + if c.addr() != other.addr() { + if c.addr() == nil || other.addr() == nil { + return false + } + if c.addr().String() != other.addr().String() { + return false + } + } return c.NetworkType() == other.NetworkType() && c.Type() == other.Type() && c.Address() == other.Address() && @@ -394,7 +403,7 @@ func (c *candidateBase) Equal(other Candidate) bool { // String makes the candidateBase printable func (c *candidateBase) String() string { - return fmt.Sprintf("%s %s %s%s", c.NetworkType(), c.Type(), net.JoinHostPort(c.Address(), strconv.Itoa(c.Port())), c.relatedAddress) + return fmt.Sprintf("%s %s %s%s (resolved: %v)", c.NetworkType(), c.Type(), net.JoinHostPort(c.Address(), strconv.Itoa(c.Port())), c.relatedAddress, c.resolvedAddr) } // LastReceived returns a time.Time indicating the last time @@ -435,6 +444,10 @@ func (c *candidateBase) addr() net.Addr { return c.resolvedAddr } +func (c *candidateBase) filterForLocationTracking() bool { + return c.isLocationTracked +} + func (c *candidateBase) agent() *Agent { return c.currAgent } @@ -551,7 +564,7 @@ func UnmarshalCandidate(raw string) (Candidate, error) { switch typ { case "host": - return NewCandidateHost(&CandidateHostConfig{"", protocol, address, port, component, priority, foundation, tcpType}) + return NewCandidateHost(&CandidateHostConfig{"", protocol, address, port, component, priority, foundation, tcpType, false}) case "srflx": return NewCandidateServerReflexive(&CandidateServerReflexiveConfig{"", protocol, address, port, component, priority, foundation, relatedAddress, relatedPort}) case "prflx": diff --git a/candidate_host.go b/candidate_host.go index 5d207dd..c14b6a7 100644 --- a/candidate_host.go +++ b/candidate_host.go @@ -4,7 +4,7 @@ package ice import ( - "net" + "net/netip" "strings" ) @@ -17,14 +17,15 @@ type CandidateHost struct { // CandidateHostConfig is the config required to create a new CandidateHost type CandidateHostConfig struct { - CandidateID string - Network string - Address string - Port int - Component uint16 - Priority uint32 - Foundation string - TCPType TCPType + CandidateID string + Network string + Address string + Port int + Component uint16 + Priority uint32 + Foundation string + TCPType TCPType + IsLocationTracked bool } // NewCandidateHost creates a new host candidate @@ -46,17 +47,18 @@ func NewCandidateHost(config *CandidateHostConfig) (*CandidateHost, error) { foundationOverride: config.Foundation, priorityOverride: config.Priority, remoteCandidateCaches: map[AddrPort]Candidate{}, + isLocationTracked: config.IsLocationTracked, }, network: config.Network, } if !strings.HasSuffix(config.Address, ".local") { - ip := net.ParseIP(config.Address) - if ip == nil { - return nil, ErrAddressParseFailed + ipAddr, err := netip.ParseAddr(config.Address) + if err != nil { + return nil, err } - if err := c.setIP(ip); err != nil { + if err := c.setIPAddr(ipAddr); err != nil { return nil, err } } else { @@ -67,14 +69,14 @@ func NewCandidateHost(config *CandidateHostConfig) (*CandidateHost, error) { return c, nil } -func (c *CandidateHost) setIP(ip net.IP) error { - networkType, err := determineNetworkType(c.network, ip) +func (c *CandidateHost) setIPAddr(addr netip.Addr) error { + networkType, err := determineNetworkType(c.network, addr) if err != nil { return err } c.candidateBase.networkType = networkType - c.candidateBase.resolvedAddr = createAddr(networkType, ip, c.port) + c.candidateBase.resolvedAddr = createAddr(networkType, addr, c.port) return nil } diff --git a/candidate_peer_reflexive.go b/candidate_peer_reflexive.go index bbcfe33..b28e9a7 100644 --- a/candidate_peer_reflexive.go +++ b/candidate_peer_reflexive.go @@ -6,7 +6,9 @@ //nolint:dupl package ice -import "net" +import ( + "net/netip" +) // CandidatePeerReflexive ... type CandidatePeerReflexive struct { @@ -28,12 +30,12 @@ type CandidatePeerReflexiveConfig struct { // NewCandidatePeerReflexive creates a new peer reflective candidate func NewCandidatePeerReflexive(config *CandidatePeerReflexiveConfig) (*CandidatePeerReflexive, error) { - ip := net.ParseIP(config.Address) - if ip == nil { - return nil, ErrAddressParseFailed + ipAddr, err := netip.ParseAddr(config.Address) + if err != nil { + return nil, err } - networkType, err := determineNetworkType(config.Network, ip) + networkType, err := determineNetworkType(config.Network, ipAddr) if err != nil { return nil, err } @@ -50,7 +52,7 @@ func NewCandidatePeerReflexive(config *CandidatePeerReflexiveConfig) (*Candidate candidateType: CandidateTypePeerReflexive, address: config.Address, port: config.Port, - resolvedAddr: createAddr(networkType, ip, config.Port), + resolvedAddr: createAddr(networkType, ipAddr, config.Port), component: config.Component, foundationOverride: config.Foundation, priorityOverride: config.Priority, diff --git a/candidate_relay.go b/candidate_relay.go index fa5297b..faf281b 100644 --- a/candidate_relay.go +++ b/candidate_relay.go @@ -5,6 +5,7 @@ package ice import ( "net" + "net/netip" ) // CandidateRelay ... @@ -38,24 +39,28 @@ func NewCandidateRelay(config *CandidateRelayConfig) (*CandidateRelay, error) { candidateID = globalCandidateIDGenerator.Generate() } - ip := net.ParseIP(config.Address) - if ip == nil { - return nil, ErrAddressParseFailed + ipAddr, err := netip.ParseAddr(config.Address) + if err != nil { + return nil, err } - networkType, err := determineNetworkType(config.Network, ip) + networkType, err := determineNetworkType(config.Network, ipAddr) if err != nil { return nil, err } return &CandidateRelay{ candidateBase: candidateBase{ - id: candidateID, - networkType: networkType, - candidateType: CandidateTypeRelay, - address: config.Address, - port: config.Port, - resolvedAddr: &net.UDPAddr{IP: ip, Port: config.Port}, + id: candidateID, + networkType: networkType, + candidateType: CandidateTypeRelay, + address: config.Address, + port: config.Port, + resolvedAddr: &net.UDPAddr{ + IP: ipAddr.AsSlice(), + Port: config.Port, + Zone: ipAddr.Zone(), + }, component: config.Component, foundationOverride: config.Foundation, priorityOverride: config.Priority, diff --git a/candidate_server_reflexive.go b/candidate_server_reflexive.go index 3a8ac0f..85d613e 100644 --- a/candidate_server_reflexive.go +++ b/candidate_server_reflexive.go @@ -3,7 +3,10 @@ package ice -import "net" +import ( + "net" + "net/netip" +) // CandidateServerReflexive ... type CandidateServerReflexive struct { @@ -25,12 +28,12 @@ type CandidateServerReflexiveConfig struct { // NewCandidateServerReflexive creates a new server reflective candidate func NewCandidateServerReflexive(config *CandidateServerReflexiveConfig) (*CandidateServerReflexive, error) { - ip := net.ParseIP(config.Address) - if ip == nil { - return nil, ErrAddressParseFailed + ipAddr, err := netip.ParseAddr(config.Address) + if err != nil { + return nil, err } - networkType, err := determineNetworkType(config.Network, ip) + networkType, err := determineNetworkType(config.Network, ipAddr) if err != nil { return nil, err } @@ -42,12 +45,16 @@ func NewCandidateServerReflexive(config *CandidateServerReflexiveConfig) (*Candi return &CandidateServerReflexive{ candidateBase: candidateBase{ - id: candidateID, - networkType: networkType, - candidateType: CandidateTypeServerReflexive, - address: config.Address, - port: config.Port, - resolvedAddr: &net.UDPAddr{IP: ip, Port: config.Port}, + id: candidateID, + networkType: networkType, + candidateType: CandidateTypeServerReflexive, + address: config.Address, + port: config.Port, + resolvedAddr: &net.UDPAddr{ + IP: ipAddr.AsSlice(), + Port: config.Port, + Zone: ipAddr.Zone(), + }, component: config.Component, foundationOverride: config.Foundation, priorityOverride: config.Priority, diff --git a/candidate_test.go b/candidate_test.go index 96ab87c..04bb17c 100644 --- a/candidate_test.go +++ b/candidate_test.go @@ -5,6 +5,7 @@ package ice import ( "net" + "strconv" "testing" "time" @@ -265,107 +266,105 @@ func TestCandidateFoundation(t *testing.T) { }).Foundation()) } +func mustCandidateHost(conf *CandidateHostConfig) Candidate { + cand, err := NewCandidateHost(conf) + if err != nil { + panic(err) + } + return cand +} + +func mustCandidateRelay(conf *CandidateRelayConfig) Candidate { + cand, err := NewCandidateRelay(conf) + if err != nil { + panic(err) + } + return cand +} + +func mustCandidateServerReflexive(conf *CandidateServerReflexiveConfig) Candidate { + cand, err := NewCandidateServerReflexive(conf) + if err != nil { + panic(err) + } + return cand +} + func TestCandidateMarshal(t *testing.T) { - for _, test := range []struct { + for idx, test := range []struct { candidate Candidate marshaled string expectError bool }{ { - &CandidateHost{ - candidateBase{ - networkType: NetworkTypeUDP6, - candidateType: CandidateTypeHost, - address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", - port: 53987, - priorityOverride: 500, - foundationOverride: "750", - }, - "", - }, + mustCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP6.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }), "750 1 udp 500 fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a 53987 typ host", false, }, { - &CandidateHost{ - candidateBase{ - networkType: NetworkTypeUDP4, - candidateType: CandidateTypeHost, - address: "10.0.75.1", - port: 53634, - }, - "", - }, + mustCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "10.0.75.1", + Port: 53634, + }), "4273957277 1 udp 2130706431 10.0.75.1 53634 typ host", false, }, { - &CandidateServerReflexive{ - candidateBase{ - networkType: NetworkTypeUDP4, - candidateType: CandidateTypeServerReflexive, - address: "191.228.238.68", - port: 53991, - relatedAddress: &CandidateRelatedAddress{"192.168.0.274", 53991}, - }, - }, + mustCandidateServerReflexive(&CandidateServerReflexiveConfig{ + Network: NetworkTypeUDP4.String(), + Address: "191.228.238.68", + Port: 53991, + RelAddr: "192.168.0.274", + RelPort: 53991, + }), "647372371 1 udp 1694498815 191.228.238.68 53991 typ srflx raddr 192.168.0.274 rport 53991", false, }, { - &CandidateRelay{ - candidateBase{ - networkType: NetworkTypeUDP4, - candidateType: CandidateTypeRelay, - address: "50.0.0.1", - port: 5000, - relatedAddress: &CandidateRelatedAddress{"192.168.0.1", 5001}, - }, - "", - nil, - }, + mustCandidateRelay(&CandidateRelayConfig{ + Network: NetworkTypeUDP4.String(), + Address: "50.0.0.1", + Port: 5000, + RelAddr: "192.168.0.1", + RelPort: 5001, + }), "848194626 1 udp 16777215 50.0.0.1 5000 typ relay raddr 192.168.0.1 rport 5001", false, }, { - &CandidateHost{ - candidateBase{ - networkType: NetworkTypeTCP4, - candidateType: CandidateTypeHost, - address: "192.168.0.196", - port: 0, - tcpType: TCPTypeActive, - }, - "", - }, + mustCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeTCP4.String(), + Address: "192.168.0.196", + Port: 0, + TCPType: TCPTypeActive, + }), "1052353102 1 tcp 2128609279 192.168.0.196 0 typ host tcptype active", false, }, { - &CandidateHost{ - candidateBase{ - networkType: NetworkTypeUDP4, - candidateType: CandidateTypeHost, - address: "e2494022-4d9a-4c1e-a750-cc48d4f8d6ee.local", - port: 60542, - }, - "", - }, + mustCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "e2494022-4d9a-4c1e-a750-cc48d4f8d6ee.local", + Port: 60542, + }), "1380287402 1 udp 2130706431 e2494022-4d9a-4c1e-a750-cc48d4f8d6ee.local 60542 typ host", false, }, // Missing Foundation { - &CandidateHost{ - candidateBase{ - networkType: NetworkTypeUDP4, - candidateType: CandidateTypeHost, - address: localhostIPStr, - port: 80, - priorityOverride: 500, - foundationOverride: " ", - }, - "", - }, + mustCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: localhostIPStr, + Port: 80, + Priority: 500, + Foundation: " ", + }), " 1 udp 500 " + localhostIPStr + " 80 typ host", false, }, @@ -384,16 +383,18 @@ func TestCandidateMarshal(t *testing.T) { {nil, "4207374051 1 udp 2130706431 10.0.75.1 53634 typ INVALID", true}, {nil, "4207374051 1 INVALID 2130706431 10.0.75.1 53634 typ host", true}, } { - actualCandidate, err := UnmarshalCandidate(test.marshaled) - if test.expectError { - require.Error(t, err) - continue - } + t.Run(strconv.Itoa(idx), func(t *testing.T) { + actualCandidate, err := UnmarshalCandidate(test.marshaled) + if test.expectError { + require.Error(t, err) + return + } - require.NoError(t, err) + require.NoError(t, err) - require.True(t, test.candidate.Equal(actualCandidate)) - require.Equal(t, test.marshaled, actualCandidate.Marshal()) + require.True(t, test.candidate.Equal(actualCandidate)) + require.Equal(t, test.marshaled, actualCandidate.Marshal()) + }) } } diff --git a/gather.go b/gather.go index 258e7fc..fe56cc7 100644 --- a/gather.go +++ b/gather.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "net" + "net/netip" "reflect" "sync" "time" @@ -133,25 +134,37 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ delete(networks, udp) } - localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, networkTypes, a.includeLoopback) + _, localAddrs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, networkTypes, a.includeLoopback) if err != nil { a.log.Warnf("Failed to iterate local interfaces, host candidates will not be gathered %s", err) return } - for _, ip := range localIPs { - mappedIP := ip + for _, addr := range localAddrs { + mappedIP := addr if a.mDNSMode != MulticastDNSModeQueryAndGather && a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeHost { - if _mappedIP, innerErr := a.extIPMapper.findExternalIP(ip.String()); innerErr == nil { - mappedIP = _mappedIP + if _mappedIP, innerErr := a.extIPMapper.findExternalIP(addr.String()); innerErr == nil { + conv, ok := netip.AddrFromSlice(_mappedIP) + if !ok { + a.log.Warnf("failed to convert mapped external IP to netip.Addr'%s'", addr.String()) + continue + } + // we'd rather have an IPv4-mapped IPv6 become IPv4 so that it is usable + mappedIP = conv.Unmap() } else { - a.log.Warnf("1:1 NAT mapping is enabled but no external IP is found for %s", ip.String()) + a.log.Warnf("1:1 NAT mapping is enabled but no external IP is found for %s", addr.String()) } } address := mappedIP.String() + var isLocationTracked bool if a.mDNSMode == MulticastDNSModeQueryAndGather { address = a.mDNSName + } else { + // Here, we are not doing multicast gathering, so we will need to skip this address so + // that we don't accidentally reveal location tracking information. Otherwise, the + // case above hides the IP behind an mDNS address. + isLocationTracked = shouldFilterLocationTrackedIP(mappedIP) } for network := range networks { @@ -174,16 +187,18 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ var muxConns []net.PacketConn if multi, ok := a.tcpMux.(AllConnsGetter); ok { a.log.Debugf("GetAllConns by ufrag: %s", a.localUfrag) - muxConns, err = multi.GetAllConns(a.localUfrag, mappedIP.To4() == nil, ip) + // Note: this is missing zone for IPv6 by just grabbing the IP slice + muxConns, err = multi.GetAllConns(a.localUfrag, mappedIP.Is6(), addr.AsSlice()) if err != nil { - a.log.Warnf("Failed to get all TCP connections by ufrag: %s %s %s", network, ip, a.localUfrag) + a.log.Warnf("Failed to get all TCP connections by ufrag: %s %s %s", network, addr, a.localUfrag) continue } } else { a.log.Debugf("GetConn by ufrag: %s", a.localUfrag) - conn, err := a.tcpMux.GetConnByUfrag(a.localUfrag, mappedIP.To4() == nil, ip) + // Note: this is missing zone for IPv6 by just grabbing the IP slice + conn, err := a.tcpMux.GetConnByUfrag(a.localUfrag, mappedIP.Is6(), addr.AsSlice()) if err != nil { - a.log.Warnf("Failed to get TCP connections by ufrag: %s %s %s", network, ip, a.localUfrag) + a.log.Warnf("Failed to get TCP connections by ufrag: %s %s %s", network, addr, a.localUfrag) continue } muxConns = []net.PacketConn{conn} @@ -194,7 +209,7 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ if tcpConn, ok := conn.LocalAddr().(*net.TCPAddr); ok { conns = append(conns, connAndPort{conn, tcpConn.Port}) } else { - a.log.Warnf("Failed to get port of connection from TCPMux: %s %s %s", network, ip, a.localUfrag) + a.log.Warnf("Failed to get port of connection from TCPMux: %s %s %s", network, addr, a.localUfrag) } } if len(conns) == 0 { @@ -205,16 +220,20 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ // Is there a way to verify that the listen address is even // accessible from the current interface. case udp: - conn, err := listenUDPInPortRange(a.net, a.log, int(a.portMax), int(a.portMin), network, &net.UDPAddr{IP: ip, Port: 0}) + conn, err := listenUDPInPortRange(a.net, a.log, int(a.portMax), int(a.portMin), network, &net.UDPAddr{ + IP: addr.AsSlice(), + Port: 0, + Zone: addr.Zone(), + }) if err != nil { - a.log.Warnf("Failed to listen %s %s", network, ip) + a.log.Warnf("Failed to listen %s %s", network, addr) continue } if udpConn, ok := conn.LocalAddr().(*net.UDPAddr); ok { conns = append(conns, connAndPort{conn, udpConn.Port}) } else { - a.log.Warnf("Failed to get port of UDPAddr from ListenUDPInPortRange: %s %s %s", network, ip, a.localUfrag) + a.log.Warnf("Failed to get port of UDPAddr from ListenUDPInPortRange: %s %s %s", network, addr, a.localUfrag) continue } } @@ -226,6 +245,9 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ Port: connAndPort.port, Component: ComponentRTP, TCPType: tcpType, + // we will still process this candidate so that we start up the right + // listeners. + IsLocationTracked: isLocationTracked, } c, err := NewCandidateHost(&hostConfig) @@ -235,7 +257,7 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ } if a.mDNSMode == MulticastDNSModeQueryAndGather { - if err = c.setIP(ip); err != nil { + if err = c.setIPAddr(addr); err != nil { closeConnAndLog(connAndPort.conn, a.log, "failed to create host candidate: %s %s %d: %v", network, mappedIP, connAndPort.port, err) continue } @@ -252,6 +274,27 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ } } +// shouldFilterLocationTrackedIP returns if this candidate IP should be filtered out from +// any candidate publishing/notification for location tracking reasons. +func shouldFilterLocationTrackedIP(candidateIP netip.Addr) bool { + // https://tools.ietf.org/html/rfc8445#section-5.1.1.1 + // Similarly, when host candidates corresponding to + // an IPv6 address generated using a mechanism that prevents location + // tracking are gathered, then host candidates corresponding to IPv6 + // link-local addresses [RFC4291] MUST NOT be gathered. + return candidateIP.Is6() && (candidateIP.IsLinkLocalUnicast() || candidateIP.IsLinkLocalMulticast()) +} + +// shouldFilterLocationTracked returns if this candidate IP should be filtered out from +// any candidate publishing/notification for location tracking reasons. +func shouldFilterLocationTracked(candidateIP net.IP) bool { + addr, ok := netip.AddrFromSlice(candidateIP) + if !ok { + return false + } + return shouldFilterLocationTrackedIP(addr) +} + func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolint:gocognit if a.udpMux == nil { return errUDPMuxDisabled @@ -286,17 +329,23 @@ func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolin } var address string + var isLocationTracked bool if a.mDNSMode == MulticastDNSModeQueryAndGather { address = a.mDNSName } else { address = candidateIP.String() + // Here, we are not doing multicast gathering, so we will need to skip this address so + // that we don't accidentally reveal location tracking information. Otherwise, the + // case above hides the IP behind an mDNS address. + isLocationTracked = shouldFilterLocationTracked(candidateIP) } hostConfig := CandidateHostConfig{ - Network: udp, - Address: address, - Port: udpAddr.Port, - Component: ComponentRTP, + Network: udp, + Address: address, + Port: udpAddr.Port, + Component: ComponentRTP, + IsLocationTracked: isLocationTracked, } // Detect a duplicate candidate before calling addCandidate(). @@ -365,6 +414,11 @@ func (a *Agent) gatherCandidatesSrflxMapped(ctx context.Context, networkTypes [] return } + if shouldFilterLocationTracked(mappedIP) { + closeConnAndLog(conn, a.log, "external IP is somehow filtered for location tracking reasons %s", mappedIP) + return + } + srflxConfig := CandidateServerReflexiveConfig{ Network: network, Address: mappedIP.String(), @@ -420,6 +474,11 @@ func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.UR return } + if shouldFilterLocationTracked(serverAddr.IP) { + a.log.Warnf("STUN host %s is somehow filtered for location tracking reasons", hostPort) + return + } + xorAddr, err := a.udpMuxSrflx.GetXORMappedAddr(serverAddr, stunGatherTimeout) if err != nil { a.log.Warnf("Failed get server reflexive address %s %s: %v", network, url, err) @@ -482,6 +541,11 @@ func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, net return } + if shouldFilterLocationTracked(serverAddr.IP) { + a.log.Warnf("STUN host %s is somehow filtered for location tracking reasons", hostPort) + return + } + conn, err := listenUDPInPortRange(a.net, a.log, int(a.portMax), int(a.portMin), network, &net.UDPAddr{IP: nil, Port: 0}) if err != nil { closeConnAndLog(conn, a.log, "failed to listen for %s: %v", serverAddr.String(), err) @@ -696,6 +760,12 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / } rAddr := relayConn.LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert + + if shouldFilterLocationTracked(rAddr.IP) { + a.log.Warnf("TURN address %s is somehow filtered for location tracking reasons", rAddr.IP) + return + } + relayConfig := CandidateRelayConfig{ Network: network, Component: ComponentRTP, diff --git a/gather_test.go b/gather_test.go index 54c8a8d..1e4b896 100644 --- a/gather_test.go +++ b/gather_test.go @@ -34,11 +34,11 @@ func TestListenUDP(t *testing.T) { a, err := NewAgent(&AgentConfig{}) require.NoError(t, err) - localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) - require.NotEqual(t, len(localIPs), 0, "localInterfaces found no interfaces, unable to test") + _, localAddrs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) + require.NotEqual(t, len(localAddrs), 0, "localInterfaces found no interfaces, unable to test") require.NoError(t, err) - ip := localIPs[0] + ip := localAddrs[0].AsSlice() conn, err := listenUDPInPortRange(a.net, a.log, 0, 0, udp, &net.UDPAddr{IP: ip, Port: 0}) require.NoError(t, err, "listenUDP error with no port restriction") diff --git a/gather_vnet_test.go b/gather_vnet_test.go index ce11e83..9fda3fd 100644 --- a/gather_vnet_test.go +++ b/gather_vnet_test.go @@ -34,7 +34,7 @@ func TestVNetGather(t *testing.T) { }) require.NoError(t, err) - localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) + _, localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) if len(localIPs) > 0 { t.Fatal("should return no local IP") } @@ -73,17 +73,17 @@ func TestVNetGather(t *testing.T) { }) require.NoError(t, err) - localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) - if len(localIPs) == 0 { + _, localAddrs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) + if len(localAddrs) == 0 { t.Fatal("should have one local IP") } require.NoError(t, err) - for _, ip := range localIPs { - if ip.IsLoopback() { + for _, addr := range localAddrs { + if addr.IsLoopback() { t.Fatal("should not return loopback IP") } - if !ipNet.Contains(ip) { + if !ipNet.Contains(addr.AsSlice()) { t.Fatal("should be contained in the CIDR") } } @@ -115,13 +115,13 @@ func TestVNetGather(t *testing.T) { t.Fatalf("Failed to create agent: %s", err) } - localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) - if len(localIPs) == 0 { + _, localAddrs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) + if len(localAddrs) == 0 { t.Fatal("localInterfaces found no interfaces, unable to test") } require.NoError(t, err) - ip := localIPs[0] + ip := localAddrs[0].AsSlice() conn, err := listenUDPInPortRange(a.net, a.log, 0, 0, udp, &net.UDPAddr{IP: ip, Port: 0}) if err != nil { @@ -385,7 +385,7 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { }) require.NoError(t, err) - localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) + _, localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) require.NoError(t, err) if len(localIPs) != 0 { @@ -405,7 +405,7 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { }) require.NoError(t, err) - localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) + _, localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) require.NoError(t, err) if len(localIPs) != 0 { @@ -425,7 +425,7 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { }) require.NoError(t, err) - localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) + _, localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) require.NoError(t, err) if len(localIPs) == 0 { diff --git a/go.mod b/go.mod index 231f46c..8163a69 100644 --- a/go.mod +++ b/go.mod @@ -7,10 +7,10 @@ require ( github.com/kr/pretty v0.1.0 // indirect github.com/pion/dtls/v2 v2.2.10 github.com/pion/logging v0.2.2 - github.com/pion/mdns/v2 v2.0.4 + github.com/pion/mdns/v2 v2.0.6 github.com/pion/randutil v0.1.0 github.com/pion/stun/v2 v2.0.0 - github.com/pion/transport/v3 v3.0.1 + github.com/pion/transport/v3 v3.0.2 github.com/pion/turn/v3 v3.0.1 github.com/stretchr/testify v1.9.0 golang.org/x/net v0.22.0 diff --git a/go.sum b/go.sum index 1e0706d..ff41ef7 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,8 @@ github.com/pion/dtls/v2 v2.2.10 h1:u2Axk+FyIR1VFTPurktB+1zoEPGIW3bmyj3LEFrXjAA= github.com/pion/dtls/v2 v2.2.10/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= -github.com/pion/mdns/v2 v2.0.4 h1:ZdK19Yd+9iPrw95rW1tTwdjmYY5O3WwmsvfX6HBBefY= -github.com/pion/mdns/v2 v2.0.4/go.mod h1:y4Y034qALR23oAJuiElt2TP1ma7b1Q/uF1oYzIePHcM= +github.com/pion/mdns/v2 v2.0.6 h1:mrqisUnOajlMKqXXXtyiBmez/0rYMFVztHU3Mg1RETQ= +github.com/pion/mdns/v2 v2.0.6/go.mod h1:y4Y034qALR23oAJuiElt2TP1ma7b1Q/uF1oYzIePHcM= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= @@ -22,8 +22,9 @@ github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLcc github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo= github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= -github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= +github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4= +github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0= github.com/pion/turn/v3 v3.0.1 h1:wLi7BTQr6/Q20R0vt/lHbjv6y4GChFtC33nkYbasoT8= github.com/pion/turn/v3 v3.0.1/go.mod h1:MrJDKgqryDyWy1/4NT9TWfXWGMC7UHT6pJIv1+gMeNE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/mdns.go b/mdns.go index a3ad899..2c10a32 100644 --- a/mdns.go +++ b/mdns.go @@ -4,11 +4,14 @@ package ice import ( + "net" + "github.com/google/uuid" "github.com/pion/logging" "github.com/pion/mdns/v2" "github.com/pion/transport/v3" "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" ) // MulticastDNSMode represents the different Multicast modes ICE can run in @@ -33,30 +36,95 @@ func generateMulticastDNSName() (string, error) { return u.String() + ".local", err } -func createMulticastDNS(n transport.Net, mDNSMode MulticastDNSMode, mDNSName string, log logging.LeveledLogger) (*mdns.Conn, MulticastDNSMode, error) { +func createMulticastDNS( + n transport.Net, + networkTypes []NetworkType, + interfaces []*transport.Interface, + includeLoopback bool, + mDNSMode MulticastDNSMode, + mDNSName string, + log logging.LeveledLogger, +) (*mdns.Conn, MulticastDNSMode, error) { if mDNSMode == MulticastDNSModeDisabled { return nil, mDNSMode, nil } - addr, mdnsErr := n.ResolveUDPAddr("udp4", mdns.DefaultAddressIPv4) + var useV4, useV6 bool + if len(networkTypes) == 0 { + useV4 = true + useV6 = true + } else { + for _, nt := range networkTypes { + if nt.IsIPv4() { + useV4 = true + continue + } + if nt.IsIPv6() { + useV6 = true + } + } + } + + addr4, mdnsErr := n.ResolveUDPAddr("udp4", mdns.DefaultAddressIPv4) + if mdnsErr != nil { + return nil, mDNSMode, mdnsErr + } + addr6, mdnsErr := n.ResolveUDPAddr("udp6", mdns.DefaultAddressIPv6) if mdnsErr != nil { return nil, mDNSMode, mdnsErr } - l, mdnsErr := n.ListenUDP("udp4", addr) - if mdnsErr != nil { + var pktConnV4 *ipv4.PacketConn + var mdns4Err error + if useV4 { + var l transport.UDPConn + l, mdns4Err = n.ListenUDP("udp4", addr4) + if mdns4Err != nil { + // If ICE fails to start MulticastDNS server just warn the user and continue + log.Errorf("Failed to enable mDNS over IPv4: (%s)", mdns4Err) + return nil, MulticastDNSModeDisabled, nil + } + pktConnV4 = ipv4.NewPacketConn(l) + } + + var pktConnV6 *ipv6.PacketConn + var mdns6Err error + if useV6 { + var l transport.UDPConn + l, mdns6Err = n.ListenUDP("udp6", addr6) + if mdns6Err != nil { + log.Errorf("Failed to enable mDNS over IPv6: (%s)", mdns6Err) + return nil, MulticastDNSModeDisabled, nil + } + pktConnV6 = ipv6.NewPacketConn(l) + } + + if mdns4Err != nil && mdns6Err != nil { // If ICE fails to start MulticastDNS server just warn the user and continue - log.Errorf("Failed to enable mDNS, continuing in mDNS disabled mode: (%s)", mdnsErr) + log.Errorf("Failed to enable mDNS, continuing in mDNS disabled mode") + //nolint:nilerr return nil, MulticastDNSModeDisabled, nil } + var ifcs []net.Interface + if interfaces != nil { + ifcs = make([]net.Interface, 0, len(ifcs)) + for _, ifc := range interfaces { + ifcs = append(ifcs, ifc.Interface) + } + } switch mDNSMode { case MulticastDNSModeQueryOnly: - conn, err := mdns.Server(ipv4.NewPacketConn(l), nil, &mdns.Config{}) + conn, err := mdns.Server(pktConnV4, pktConnV6, &mdns.Config{ + Interfaces: ifcs, + IncludeLoopback: includeLoopback, + }) return conn, mDNSMode, err case MulticastDNSModeQueryAndGather: - conn, err := mdns.Server(ipv4.NewPacketConn(l), nil, &mdns.Config{ - LocalNames: []string{mDNSName}, + conn, err := mdns.Server(pktConnV4, pktConnV6, &mdns.Config{ + Interfaces: ifcs, + IncludeLoopback: includeLoopback, + LocalNames: []string{mDNSName}, }) return conn, mDNSMode, err default: diff --git a/mdns_test.go b/mdns_test.go index a82d6b1..617492b 100644 --- a/mdns_test.go +++ b/mdns_test.go @@ -22,30 +22,51 @@ func TestMulticastDNSOnlyConnection(t *testing.T) { // Limit runtime in case of deadlocks defer test.TimeOut(time.Second * 30).Stop() - cfg := &AgentConfig{ - NetworkTypes: []NetworkType{NetworkTypeUDP4}, - CandidateTypes: []CandidateType{CandidateTypeHost}, - MulticastDNSMode: MulticastDNSModeQueryAndGather, + type testCase struct { + Name string + NetworkTypes []NetworkType } - aAgent, err := NewAgent(cfg) - require.NoError(t, err) + testCases := []testCase{ + {Name: "UDP4", NetworkTypes: []NetworkType{NetworkTypeUDP4}}, + } - aNotifier, aConnected := onConnected() - require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) + if ipv6Available(t) { + testCases = append(testCases, + testCase{Name: "UDP6", NetworkTypes: []NetworkType{NetworkTypeUDP6}}, + testCase{Name: "UDP46", NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}}, + ) + } - bAgent, err := NewAgent(cfg) - require.NoError(t, err) + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + cfg := &AgentConfig{ + NetworkTypes: tc.NetworkTypes, + CandidateTypes: []CandidateType{CandidateTypeHost}, + MulticastDNSMode: MulticastDNSModeQueryAndGather, + InterfaceFilter: problematicNetworkInterfaces, + } - bNotifier, bConnected := onConnected() - require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) + aAgent, err := NewAgent(cfg) + require.NoError(t, err) - connect(aAgent, bAgent) - <-aConnected - <-bConnected + aNotifier, aConnected := onConnected() + require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) - require.NoError(t, aAgent.Close()) - require.NoError(t, bAgent.Close()) + bAgent, err := NewAgent(cfg) + require.NoError(t, err) + + bNotifier, bConnected := onConnected() + require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) + + connect(aAgent, bAgent) + <-aConnected + <-bConnected + + require.NoError(t, aAgent.Close()) + require.NoError(t, bAgent.Close()) + }) + } } func TestMulticastDNSMixedConnection(t *testing.T) { @@ -54,32 +75,54 @@ func TestMulticastDNSMixedConnection(t *testing.T) { // Limit runtime in case of deadlocks defer test.TimeOut(time.Second * 30).Stop() - aAgent, err := NewAgent(&AgentConfig{ - NetworkTypes: []NetworkType{NetworkTypeUDP4}, - CandidateTypes: []CandidateType{CandidateTypeHost}, - MulticastDNSMode: MulticastDNSModeQueryAndGather, - }) - require.NoError(t, err) + type testCase struct { + Name string + NetworkTypes []NetworkType + } - aNotifier, aConnected := onConnected() - require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) + testCases := []testCase{ + {Name: "UDP4", NetworkTypes: []NetworkType{NetworkTypeUDP4}}, + } - bAgent, err := NewAgent(&AgentConfig{ - NetworkTypes: []NetworkType{NetworkTypeUDP4}, - CandidateTypes: []CandidateType{CandidateTypeHost}, - MulticastDNSMode: MulticastDNSModeQueryOnly, - }) - require.NoError(t, err) + if ipv6Available(t) { + testCases = append(testCases, + testCase{Name: "UDP6", NetworkTypes: []NetworkType{NetworkTypeUDP6}}, + testCase{Name: "UDP46", NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}}, + ) + } - bNotifier, bConnected := onConnected() - require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + aAgent, err := NewAgent(&AgentConfig{ + NetworkTypes: tc.NetworkTypes, + CandidateTypes: []CandidateType{CandidateTypeHost}, + MulticastDNSMode: MulticastDNSModeQueryAndGather, + InterfaceFilter: problematicNetworkInterfaces, + }) + require.NoError(t, err) - connect(aAgent, bAgent) - <-aConnected - <-bConnected + aNotifier, aConnected := onConnected() + require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) - require.NoError(t, aAgent.Close()) - require.NoError(t, bAgent.Close()) + bAgent, err := NewAgent(&AgentConfig{ + NetworkTypes: tc.NetworkTypes, + CandidateTypes: []CandidateType{CandidateTypeHost}, + MulticastDNSMode: MulticastDNSModeQueryOnly, + InterfaceFilter: problematicNetworkInterfaces, + }) + require.NoError(t, err) + + bNotifier, bConnected := onConnected() + require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) + + connect(aAgent, bAgent) + <-aConnected + <-bConnected + + require.NoError(t, aAgent.Close()) + require.NoError(t, bAgent.Close()) + }) + } } func TestMulticastDNSStaticHostName(t *testing.T) { @@ -87,32 +130,54 @@ func TestMulticastDNSStaticHostName(t *testing.T) { defer test.TimeOut(time.Second * 30).Stop() - _, err := NewAgent(&AgentConfig{ - NetworkTypes: []NetworkType{NetworkTypeUDP4}, - CandidateTypes: []CandidateType{CandidateTypeHost}, - MulticastDNSMode: MulticastDNSModeQueryAndGather, - MulticastDNSHostName: "invalidHostName", - }) - require.Equal(t, err, ErrInvalidMulticastDNSHostName) + type testCase struct { + Name string + NetworkTypes []NetworkType + } - agent, err := NewAgent(&AgentConfig{ - NetworkTypes: []NetworkType{NetworkTypeUDP4}, - CandidateTypes: []CandidateType{CandidateTypeHost}, - MulticastDNSMode: MulticastDNSModeQueryAndGather, - MulticastDNSHostName: "validName.local", - }) - require.NoError(t, err) + testCases := []testCase{ + {Name: "UDP4", NetworkTypes: []NetworkType{NetworkTypeUDP4}}, + } - correctHostName, resolveFunc := context.WithCancel(context.Background()) - require.NoError(t, agent.OnCandidate(func(c Candidate) { - if c != nil && c.Address() == "validName.local" { - resolveFunc() - } - })) + if ipv6Available(t) { + testCases = append(testCases, + testCase{Name: "UDP6", NetworkTypes: []NetworkType{NetworkTypeUDP6}}, + testCase{Name: "UDP46", NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}}, + ) + } - require.NoError(t, agent.GatherCandidates()) - <-correctHostName.Done() - require.NoError(t, agent.Close()) + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + _, err := NewAgent(&AgentConfig{ + NetworkTypes: tc.NetworkTypes, + CandidateTypes: []CandidateType{CandidateTypeHost}, + MulticastDNSMode: MulticastDNSModeQueryAndGather, + MulticastDNSHostName: "invalidHostName", + InterfaceFilter: problematicNetworkInterfaces, + }) + require.Equal(t, err, ErrInvalidMulticastDNSHostName) + + agent, err := NewAgent(&AgentConfig{ + NetworkTypes: tc.NetworkTypes, + CandidateTypes: []CandidateType{CandidateTypeHost}, + MulticastDNSMode: MulticastDNSModeQueryAndGather, + MulticastDNSHostName: "validName.local", + InterfaceFilter: problematicNetworkInterfaces, + }) + require.NoError(t, err) + + correctHostName, resolveFunc := context.WithCancel(context.Background()) + require.NoError(t, agent.OnCandidate(func(c Candidate) { + if c != nil && c.Address() == "validName.local" { + resolveFunc() + } + })) + + require.NoError(t, agent.GatherCandidates()) + <-correctHostName.Done() + require.NoError(t, agent.Close()) + }) + } } func TestGenerateMulticastDNSName(t *testing.T) { diff --git a/net.go b/net.go index f686d41..6e740a7 100644 --- a/net.go +++ b/net.go @@ -5,6 +5,7 @@ package ice import ( "net" + "net/netip" "github.com/pion/logging" "github.com/pion/transport/v3" @@ -12,12 +13,15 @@ import ( // The conditions of invalidation written below are defined in // https://tools.ietf.org/html/rfc8445#section-5.1.1.1 -func isSupportedIPv6(ip net.IP) bool { +// It is partial because the link-local check is done later in various gather local +// candidate methods which conditionally accept IPv6 based on usage of mDNS or not. +func isSupportedIPv6Partial(ip net.IP) bool { if len(ip) != net.IPv6len || + // Deprecated IPv4-compatible IPv6 addresses [RFC4291] and IPv6 site- + // local unicast addresses [RFC3879] MUST NOT be included in the + // address candidates. isZeros(ip[0:12]) || // !(IPv4-compatible IPv6) - ip[0] == 0xfe && ip[1]&0xc0 == 0xc0 || // !(IPv6 site-local unicast) - ip.IsLinkLocalUnicast() || - ip.IsLinkLocalMulticast() { + ip[0] == 0xfe && ip[1]&0xc0 == 0xc0 { // !(IPv6 site-local unicast) return false } return true @@ -32,21 +36,35 @@ func isZeros(ip net.IP) bool { return true } -func localInterfaces(n transport.Net, interfaceFilter func(string) bool, ipFilter func(net.IP) bool, networkTypes []NetworkType, includeLoopback bool) ([]net.IP, error) { //nolint:gocognit - ips := []net.IP{} +//nolint:gocognit +func localInterfaces( + n transport.Net, + interfaceFilter func(string) bool, + ipFilter func(net.IP) bool, + networkTypes []NetworkType, + includeLoopback bool, +) ([]*transport.Interface, []netip.Addr, error) { + ipAddrs := []netip.Addr{} ifaces, err := n.Interfaces() if err != nil { - return ips, err + return nil, ipAddrs, err } - var IPv4Requested, IPv6Requested bool - for _, typ := range networkTypes { - if typ.IsIPv4() { - IPv4Requested = true - } + filteredIfaces := make([]*transport.Interface, 0, len(ifaces)) - if typ.IsIPv6() { - IPv6Requested = true + var ipV4Requested, ipv6Requested bool + if len(networkTypes) == 0 { + ipV4Requested = true + ipv6Requested = true + } else { + for _, typ := range networkTypes { + if typ.IsIPv4() { + ipV4Requested = true + } + + if typ.IsIPv6() { + ipv6Requested = true + } } } @@ -62,41 +80,41 @@ func localInterfaces(n transport.Net, interfaceFilter func(string) bool, ipFilte continue } - addrs, err := iface.Addrs() + ifaceAddrs, err := iface.Addrs() if err != nil { continue } - for _, addr := range addrs { - var ip net.IP - switch addr := addr.(type) { - case *net.IPNet: - ip = addr.IP - case *net.IPAddr: - ip = addr.IP - } - if ip == nil || (ip.IsLoopback() && !includeLoopback) { + atLeastOneAddr := false + for _, addr := range ifaceAddrs { + ipAddr, _, _, err := parseAddrFromIface(addr, iface.Name) + if err != nil || (ipAddr.IsLoopback() && !includeLoopback) { continue } - - if ipv4 := ip.To4(); ipv4 == nil { - if !IPv6Requested { + if ipAddr.Is6() { + if !ipv6Requested { continue - } else if !isSupportedIPv6(ip) { + } else if !isSupportedIPv6Partial(ipAddr.AsSlice()) { continue } - } else if !IPv4Requested { + } else if !ipV4Requested { continue } - if ipFilter != nil && !ipFilter(ip) { + if ipFilter != nil && !ipFilter(ipAddr.AsSlice()) { continue } - ips = append(ips, ip) + atLeastOneAddr = true + ipAddrs = append(ipAddrs, ipAddr) + } + + if atLeastOneAddr { + ifaceCopy := iface + filteredIfaces = append(filteredIfaces, ifaceCopy) } } - return ips, nil + return filteredIfaces, ipAddrs, nil } func listenUDPInPortRange(n transport.Net, log logging.LeveledLogger, portMax, portMin int, network string, lAddr *net.UDPAddr) (transport.UDPConn, error) { diff --git a/net_test.go b/net_test.go index 949fd81..12f0a9e 100644 --- a/net_test.go +++ b/net_test.go @@ -5,40 +5,68 @@ package ice import ( "net" + "net/netip" + "strings" "testing" "github.com/stretchr/testify/require" ) -func TestIsSupportedIPv6(t *testing.T) { - if isSupportedIPv6(net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1}) { - t.Errorf("isSupportedIPv6 return true with IPv4-compatible IPv6 address") +func TestIsSupportedIPv6Partial(t *testing.T) { + if isSupportedIPv6Partial(net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1}) { + t.Errorf("isSupportedIPv6Partial returned true with IPv4-compatible IPv6 address") } - if isSupportedIPv6(net.ParseIP("fec0::2333")) { - t.Errorf("isSupportedIPv6 return true with IPv6 site-local unicast address") + if isSupportedIPv6Partial(net.ParseIP("fec0::2333")) { + t.Errorf("isSupportedIPv6Partial returned true with IPv6 site-local unicast address") } - if isSupportedIPv6(net.ParseIP("fe80::2333")) { - t.Errorf("isSupportedIPv6 return true with IPv6 link-local address") + if !isSupportedIPv6Partial(net.ParseIP("fe80::2333")) { + t.Errorf("isSupportedIPv6Partial returned false with IPv6 link-local address") } - if isSupportedIPv6(net.ParseIP("ff02::2333")) { - t.Errorf("isSupportedIPv6 return true with IPv6 link-local multicast address") + if !isSupportedIPv6Partial(net.ParseIP("ff02::2333")) { + t.Errorf("isSupportedIPv6Partial returned false with IPv6 link-local multicast address") } - if !isSupportedIPv6(net.ParseIP("2001::1")) { - t.Errorf("isSupportedIPv6 return false with IPv6 global unicast address") + if !isSupportedIPv6Partial(net.ParseIP("2001::1")) { + t.Errorf("isSupportedIPv6Partial returned false with IPv6 global unicast address") } } func TestCreateAddr(t *testing.T) { - ipv4 := net.IP{127, 0, 0, 1} - ipv6 := net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} + ipv4 := mustAddr(t, net.IP{127, 0, 0, 1}) + ipv6 := mustAddr(t, net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}) port := 9000 - require.Equal(t, &net.UDPAddr{IP: ipv4, Port: port}, createAddr(NetworkTypeUDP4, ipv4, port)) - require.Equal(t, &net.UDPAddr{IP: ipv6, Port: port}, createAddr(NetworkTypeUDP6, ipv6, port)) - require.Equal(t, &net.TCPAddr{IP: ipv4, Port: port}, createAddr(NetworkTypeTCP4, ipv4, port)) - require.Equal(t, &net.TCPAddr{IP: ipv6, Port: port}, createAddr(NetworkTypeTCP6, ipv6, port)) + require.Equal(t, &net.UDPAddr{IP: ipv4.AsSlice(), Port: port}, createAddr(NetworkTypeUDP4, ipv4, port)) + require.Equal(t, &net.UDPAddr{IP: ipv6.AsSlice(), Port: port}, createAddr(NetworkTypeUDP6, ipv6, port)) + require.Equal(t, &net.TCPAddr{IP: ipv4.AsSlice(), Port: port}, createAddr(NetworkTypeTCP4, ipv4, port)) + require.Equal(t, &net.TCPAddr{IP: ipv6.AsSlice(), Port: port}, createAddr(NetworkTypeTCP6, ipv6, port)) +} + +func problematicNetworkInterfaces(s string) bool { + defaultDockerBridgeNetwork := strings.Contains(s, "docker") + customDockerBridgeNetwork := strings.Contains(s, "br-") + + // Apple filters + accessPoint := strings.Contains(s, "ap") + appleWirelessDirectLink := strings.Contains(s, "awdl") + appleLowLatencyWLANInterface := strings.Contains(s, "llw") + appleTunnelingInterface := strings.Contains(s, "utun") + return !defaultDockerBridgeNetwork && + !customDockerBridgeNetwork && + !accessPoint && + !appleWirelessDirectLink && + !appleLowLatencyWLANInterface && + !appleTunnelingInterface +} + +func mustAddr(t *testing.T, ip net.IP) netip.Addr { + t.Helper() + addr, ok := netip.AddrFromSlice(ip) + if !ok { + t.Fatal(ipConvertError{ip}) + } + return addr } diff --git a/networktype.go b/networktype.go index 57df186..376b56c 100644 --- a/networktype.go +++ b/networktype.go @@ -5,7 +5,7 @@ package ice import ( "fmt" - "net" + "net/netip" "strings" ) @@ -116,18 +116,18 @@ func (t NetworkType) IsIPv6() bool { // determineNetworkType determines the type of network based on // the short network string and an IP address. -func determineNetworkType(network string, ip net.IP) (NetworkType, error) { - ipv4 := ip.To4() != nil - +func determineNetworkType(network string, ip netip.Addr) (NetworkType, error) { + // we'd rather have an IPv4-mapped IPv6 become IPv4 so that it is usable. + ip = ip.Unmap() switch { case strings.HasPrefix(strings.ToLower(network), udp): - if ipv4 { + if ip.Is4() { return NetworkTypeUDP4, nil } return NetworkTypeUDP6, nil case strings.HasPrefix(strings.ToLower(network), tcp): - if ipv4 { + if ip.Is4() { return NetworkTypeTCP4, nil } return NetworkTypeTCP6, nil diff --git a/networktype_test.go b/networktype_test.go index eb4a2e4..d327af6 100644 --- a/networktype_test.go +++ b/networktype_test.go @@ -45,7 +45,7 @@ func TestNetworkTypeParsing_Success(t *testing.T) { NetworkTypeUDP6, }, } { - actual, err := determineNetworkType(test.inNetwork, test.inIP) + actual, err := determineNetworkType(test.inNetwork, mustAddr(t, test.inIP)) if err != nil { t.Errorf("NetworkTypeParsing failed: %v", err) } @@ -70,7 +70,7 @@ func TestNetworkTypeParsing_Failure(t *testing.T) { ipv6, }, } { - actual, err := determineNetworkType(test.inNetwork, test.inIP) + actual, err := determineNetworkType(test.inNetwork, mustAddr(t, test.inIP)) if err == nil { t.Errorf("NetworkTypeParsing should fail: '%s' -- input:%s actual:%s", test.name, test.inNetwork, actual) diff --git a/tcp_mux.go b/tcp_mux.go index c5608b3..dfedad1 100644 --- a/tcp_mux.go +++ b/tcp_mux.go @@ -142,6 +142,7 @@ func (m *TCPMuxDefault) createConn(ufrag string, isIPv6 bool, local net.IP, from return nil, ErrGetTransportAddress } localAddr := *addr + // Note: this is missing zone for IPv6 localAddr.IP = local var alive time.Duration @@ -169,13 +170,15 @@ func (m *TCPMuxDefault) createConn(ufrag string, isIPv6 bool, local net.IP, from m.connsIPv4[ufrag] = conns } } - conns[ipAddr(local.String())] = conn + // Note: this is missing zone for IPv6 + connKey := ipAddr(local.String()) + conns[connKey] = conn m.wg.Add(1) go func() { defer m.wg.Done() <-conn.CloseChannel() - m.removeConnByUfragAndLocalHost(ufrag, local) + m.removeConnByUfragAndLocalHost(ufrag, connKey) }() return conn, nil @@ -259,6 +262,7 @@ func (m *TCPMuxDefault) handleConn(conn net.Conn) { return } m.mu.Lock() + packetConn, ok := m.getConn(ufrag, isIPv6, localAddr.IP) if !ok { packetConn, err = m.createConn(ufrag, isIPv6, localAddr.IP, true) @@ -334,15 +338,14 @@ func (m *TCPMuxDefault) RemoveConnByUfrag(ufrag string) { } } -func (m *TCPMuxDefault) removeConnByUfragAndLocalHost(ufrag string, local net.IP) { +func (m *TCPMuxDefault) removeConnByUfragAndLocalHost(ufrag string, localIPAddr ipAddr) { removedConns := make([]*tcpPacketConn, 0, 4) - localIP := ipAddr(local.String()) // Keep lock section small to avoid deadlock with conn lock m.mu.Lock() if conns, ok := m.connsIPv4[ufrag]; ok { - if conn, ok := conns[localIP]; ok { - delete(conns, localIP) + if conn, ok := conns[localIPAddr]; ok { + delete(conns, localIPAddr) if len(conns) == 0 { delete(m.connsIPv4, ufrag) } @@ -350,8 +353,8 @@ func (m *TCPMuxDefault) removeConnByUfragAndLocalHost(ufrag string, local net.IP } } if conns, ok := m.connsIPv6[ufrag]; ok { - if conn, ok := conns[localIP]; ok { - delete(conns, localIP) + if conn, ok := conns[localIPAddr]; ok { + delete(conns, localIPAddr) if len(conns) == 0 { delete(m.connsIPv6, ufrag) } @@ -375,7 +378,9 @@ func (m *TCPMuxDefault) getConn(ufrag string, isIPv6 bool, local net.IP) (val *t conns, ok = m.connsIPv4[ufrag] } if conns != nil { - val, ok = conns[ipAddr(local.String())] + // Note: this is missing zone for IPv6 + connKey := ipAddr(local.String()) + val, ok = conns[connKey] } return diff --git a/tcp_mux_multi.go b/tcp_mux_multi.go index e32acbf..71fc570 100644 --- a/tcp_mux_multi.go +++ b/tcp_mux_multi.go @@ -3,7 +3,9 @@ package ice -import "net" +import ( + "net" +) // AllConnsGetter allows multiple fixed TCP ports to be used, // each of which is multiplexed like TCPMux. AllConnsGetter also acts as diff --git a/tcp_mux_test.go b/tcp_mux_test.go index c6d9748..dc8dd8e 100644 --- a/tcp_mux_test.go +++ b/tcp_mux_test.go @@ -62,7 +62,10 @@ func TestTCPMux_Recv(t *testing.T) { n, err := writeStreamingPacket(conn, msg.Raw) require.NoError(t, err, "error writing TCP STUN packet") - pktConn, err := tcpMux.GetConnByUfrag("myufrag", false, listener.Addr().(*net.TCPAddr).IP) + listenerAddr, ok := listener.Addr().(*net.TCPAddr) + require.True(t, ok) + + pktConn, err := tcpMux.GetConnByUfrag("myufrag", false, listenerAddr.IP) require.NoError(t, err, "error retrieving muxed connection for ufrag") defer func() { _ = pktConn.Close() @@ -111,12 +114,15 @@ func TestTCPMux_NoDeadlockWhenClosingUnusedPacketConn(t *testing.T) { _ = tcpMux.Close() }() - _, err = tcpMux.GetConnByUfrag("test", false, listener.Addr().(*net.TCPAddr).IP) + listenerAddr, ok := listener.Addr().(*net.TCPAddr) + require.True(t, ok) + + _, err = tcpMux.GetConnByUfrag("test", false, listenerAddr.IP) require.NoError(t, err, "error getting conn by ufrag") require.NoError(t, tcpMux.Close(), "error closing tcpMux") - conn, err := tcpMux.GetConnByUfrag("test", false, listener.Addr().(*net.TCPAddr).IP) + conn, err := tcpMux.GetConnByUfrag("test", false, listenerAddr.IP) require.Nil(t, conn, "should receive nil because mux is closed") require.Equal(t, io.ErrClosedPipe, err, "should receive error because mux is closed") } @@ -231,7 +237,10 @@ func TestTCPMux_NoLeakForConnectionFromStun(t *testing.T) { // wait for the connection to be created time.Sleep(100 * time.Millisecond) - pktConn, err := tcpMux.GetConnByUfrag("myufrag2", false, listener.Addr().(*net.TCPAddr).IP) + listenerAddr, ok := listener.Addr().(*net.TCPAddr) + require.True(t, ok) + + pktConn, err := tcpMux.GetConnByUfrag("myufrag2", false, listenerAddr.IP) require.NoError(t, err, "error retrieving muxed connection for ufrag") defer func() { _ = pktConn.Close() diff --git a/transport_test.go b/transport_test.go index 216ee8b..c7907a1 100644 --- a/transport_test.go +++ b/transport_test.go @@ -9,6 +9,7 @@ package ice import ( "context" "net" + "net/netip" "sync" "testing" "time" @@ -175,13 +176,20 @@ func gatherAndExchangeCandidates(aAgent, bAgent *Agent) { candidates, err := aAgent.GetLocalCandidates() check(err) + for _, c := range candidates { + if addr, parseErr := netip.ParseAddr(c.Address()); parseErr == nil { + if shouldFilterLocationTrackedIP(addr) { + panic(addr) + } + } candidateCopy, copyErr := c.copy() check(copyErr) check(bAgent.AddRemoteCandidate(candidateCopy)) } candidates, err = bAgent.GetLocalCandidates() + check(err) for _, c := range candidates { candidateCopy, copyErr := c.copy() diff --git a/udp_mux.go b/udp_mux.go index dc45458..40e8ad9 100644 --- a/udp_mux.go +++ b/udp_mux.go @@ -69,19 +69,19 @@ func NewUDPMuxDefault(params UDPMuxParams) *UDPMuxDefault { } var localAddrsForUnspecified []net.Addr - if addr, ok := params.UDPConn.LocalAddr().(*net.UDPAddr); !ok { + if udpAddr, ok := params.UDPConn.LocalAddr().(*net.UDPAddr); !ok { params.Logger.Errorf("LocalAddr is not a net.UDPAddr, got %T", params.UDPConn.LocalAddr()) - } else if ok && addr.IP.IsUnspecified() { + } else if ok && udpAddr.IP.IsUnspecified() { // For unspecified addresses, the correct behavior is to return errListenUnspecified, but // it will break the applications that are already using unspecified UDP connection // with UDPMuxDefault, so print a warn log and create a local address list for mux. params.Logger.Warn("UDPMuxDefault should not listening on unspecified address, use NewMultiUDPMuxFromPort instead") var networks []NetworkType switch { - case addr.IP.To4() != nil: + case udpAddr.IP.To4() != nil: networks = []NetworkType{NetworkTypeUDP4} - case addr.IP.To16() != nil: + case udpAddr.IP.To16() != nil: networks = []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6} default: @@ -95,10 +95,14 @@ func NewUDPMuxDefault(params UDPMuxParams) *UDPMuxDefault { } } - ips, err := localInterfaces(params.Net, nil, nil, networks, true) + _, addrs, err := localInterfaces(params.Net, nil, nil, networks, true) if err == nil { - for _, ip := range ips { - localAddrsForUnspecified = append(localAddrsForUnspecified, &net.UDPAddr{IP: ip, Port: addr.Port}) + for _, addr := range addrs { + localAddrsForUnspecified = append(localAddrsForUnspecified, &net.UDPAddr{ + IP: addr.AsSlice(), + Port: udpAddr.Port, + Zone: addr.Zone(), + }) } } else { params.Logger.Errorf("Failed to get local interfaces for unspecified addr: %v", err) @@ -304,7 +308,7 @@ func (m *UDPMuxDefault) connWorker() { logger.Errorf("Underlying PacketConn did not return a UDPAddr") return } - udpAddr, err := newIPPort(netUDPAddr.IP, uint16(netUDPAddr.Port)) + udpAddr, err := newIPPort(netUDPAddr.IP, netUDPAddr.Zone, uint16(netUDPAddr.Port)) if err != nil { logger.Errorf("Failed to create a new IP/Port host pair") return @@ -378,14 +382,14 @@ type ipPort struct { // newIPPort create a custom type of address based on netip.Addr and // port. The underlying ip address passed is converted to IPv6 format // to simplify ip address handling -func newIPPort(ip net.IP, port uint16) (ipPort, error) { +func newIPPort(ip net.IP, zone string, port uint16) (ipPort, error) { n, ok := netip.AddrFromSlice(ip.To16()) if !ok { return ipPort{}, errInvalidIPAddress } return ipPort{ - addr: n, + addr: n.WithZone(zone), port: port, }, nil } diff --git a/udp_mux_multi.go b/udp_mux_multi.go index c46db9b..2594cb0 100644 --- a/udp_mux_multi.go +++ b/udp_mux_multi.go @@ -90,14 +90,18 @@ func NewMultiUDPMuxFromPort(port int, opts ...UDPMuxFromPortOption) (*MultiUDPMu } } - ips, err := localInterfaces(params.net, params.ifFilter, params.ipFilter, params.networks, params.includeLoopback) + _, addrs, err := localInterfaces(params.net, params.ifFilter, params.ipFilter, params.networks, params.includeLoopback) if err != nil { return nil, err } - conns := make([]net.PacketConn, 0, len(ips)) - for _, ip := range ips { - conn, listenErr := params.net.ListenUDP("udp", &net.UDPAddr{IP: ip, Port: port}) + conns := make([]net.PacketConn, 0, len(addrs)) + for _, addr := range addrs { + conn, listenErr := params.net.ListenUDP("udp", &net.UDPAddr{ + IP: addr.AsSlice(), + Port: port, + Zone: addr.Zone(), + }) if listenErr != nil { err = listenErr break diff --git a/udp_mux_multi_test.go b/udp_mux_multi_test.go index bb12022..f5611be 100644 --- a/udp_mux_multi_test.go +++ b/udp_mux_multi_test.go @@ -8,7 +8,6 @@ package ice import ( "net" - "strings" "sync" "testing" "time" @@ -114,11 +113,7 @@ func TestUnspecifiedUDPMux(t *testing.T) { defer test.TimeOut(time.Second * 30).Stop() muxPort := 7778 - udpMuxMulti, err := NewMultiUDPMuxFromPort(muxPort, UDPMuxFromPortWithInterfaceFilter(func(s string) bool { - defaultDockerBridgeNetwork := strings.Contains(s, "docker") - customDockerBridgeNetwork := strings.Contains(s, "br-") - return !defaultDockerBridgeNetwork && !customDockerBridgeNetwork - })) + udpMuxMulti, err := NewMultiUDPMuxFromPort(muxPort, UDPMuxFromPortWithInterfaceFilter(problematicNetworkInterfaces)) require.NoError(t, err) require.GreaterOrEqual(t, len(udpMuxMulti.muxes), 1, "at least have 1 muxes") diff --git a/udp_mux_test.go b/udp_mux_test.go index 5f8b1e0..8b64f65 100644 --- a/udp_mux_test.go +++ b/udp_mux_test.go @@ -50,13 +50,31 @@ func TestUDPMux(t *testing.T) { network string } - for _, subTest := range []testCase{ + testCases := []testCase{ {name: "IPv4loopback", conn: conn4, network: udp4}, {name: "IPv6loopback", conn: conn6, network: udp6}, {name: "Unspecified", conn: connUnspecified, network: udp}, {name: "IPv4Unspecified", conn: conn4Unspecified, network: udp4}, {name: "IPv6Unspecified", conn: conn6Unspecified, network: udp6}, - } { + } + + if ipv6Available(t) { + addr6 := getLocalIPAddress(t, NetworkTypeUDP6) + + conn6Unspecified, listenEerr := net.ListenUDP(udp, &net.UDPAddr{ + IP: addr6.AsSlice(), + Zone: addr6.Zone(), + }) + if listenEerr != nil { + t.Log("IPv6 is not supported on this machine") + } + + testCases = append(testCases, + testCase{name: "IPv6Specified", conn: conn6Unspecified, network: udp6}, + ) + } + + for _, subTest := range testCases { network, conn := subTest.network, subTest.conn if udpConn, ok := conn.(*net.UDPConn); !ok || udpConn == nil { continue diff --git a/udp_muxed_conn.go b/udp_muxed_conn.go index fb05e23..0244d13 100644 --- a/udp_muxed_conn.go +++ b/udp_muxed_conn.go @@ -86,7 +86,7 @@ func (c *udpMuxedConn) WriteTo(buf []byte, rAddr net.Addr) (n int, err error) { return 0, errFailedToCastUDPAddr } - ipAndPort, err := newIPPort(netUDPAddr.IP, uint16(netUDPAddr.Port)) + ipAndPort, err := newIPPort(netUDPAddr.IP, netUDPAddr.Zone, uint16(netUDPAddr.Port)) if err != nil { return 0, err } From 0bdcf93c6e13f56497ec5adf953431d4edc8409e Mon Sep 17 00:00:00 2001 From: Pion <59523206+pionbot@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:41:07 +0000 Subject: [PATCH 023/114] Update CI configs to v0.11.7 Update lint scripts and CI configs. --- .golangci.yml | 7 +++---- .reuse/dep5 | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 6dd80c8..e06de4d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -3,7 +3,8 @@ linters-settings: govet: - check-shadowing: true + enable: + - shadow misspell: locale: US exhaustive: @@ -110,6 +111,7 @@ linters: issues: exclude-use-default: false + exclude-dirs-use-default: false exclude-rules: # Allow complex tests and examples, better to be self contained - path: (examples|main\.go|_test\.go) @@ -121,6 +123,3 @@ issues: - path: cmd linters: - forbidigo - -run: - skip-dirs-use-default: false diff --git a/.reuse/dep5 b/.reuse/dep5 index 717f0c1..eb7fac2 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -2,7 +2,7 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Pion Source: https://github.com/pion/ -Files: README.md DESIGN.md **/README.md AUTHORS.txt renovate.json go.mod go.sum **/go.mod **/go.sum .eslintrc.json package.json examples/examples.json +Files: README.md DESIGN.md **/README.md AUTHORS.txt renovate.json go.mod go.sum **/go.mod **/go.sum .eslintrc.json package.json examples.json sfu-ws/flutter/.gitignore sfu-ws/flutter/pubspec.yaml c-data-channels/webrtc.h examples/examples.json Copyright: 2023 The Pion community License: MIT From bca4f2f2a1aa9f722a4f253c092cbb70a5c9d479 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 2 Apr 2024 17:36:56 +0000 Subject: [PATCH 024/114] Update module github.com/pion/mdns/v2 to v2.0.7 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8163a69..429353c 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/kr/pretty v0.1.0 // indirect github.com/pion/dtls/v2 v2.2.10 github.com/pion/logging v0.2.2 - github.com/pion/mdns/v2 v2.0.6 + github.com/pion/mdns/v2 v2.0.7 github.com/pion/randutil v0.1.0 github.com/pion/stun/v2 v2.0.0 github.com/pion/transport/v3 v3.0.2 diff --git a/go.sum b/go.sum index ff41ef7..0188f17 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,8 @@ github.com/pion/dtls/v2 v2.2.10 h1:u2Axk+FyIR1VFTPurktB+1zoEPGIW3bmyj3LEFrXjAA= github.com/pion/dtls/v2 v2.2.10/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= -github.com/pion/mdns/v2 v2.0.6 h1:mrqisUnOajlMKqXXXtyiBmez/0rYMFVztHU3Mg1RETQ= -github.com/pion/mdns/v2 v2.0.6/go.mod h1:y4Y034qALR23oAJuiElt2TP1ma7b1Q/uF1oYzIePHcM= +github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= +github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= From a12f670c79ea6258afa6f98e6d7564e89ea03c20 Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Tue, 2 Apr 2024 22:21:20 -0400 Subject: [PATCH 025/114] Update go.mod version to 1.19 Relates to pion/webrtc#2292 --- go.mod | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 429353c..ecaf7c5 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,9 @@ module github.com/pion/ice/v3 -go 1.13 +go 1.19 require ( github.com/google/uuid v1.6.0 - github.com/kr/pretty v0.1.0 // indirect github.com/pion/dtls/v2 v2.2.10 github.com/pion/logging v0.2.2 github.com/pion/mdns/v2 v2.0.7 @@ -14,5 +13,15 @@ require ( github.com/pion/turn/v3 v3.0.1 github.com/stretchr/testify v1.9.0 golang.org/x/net v0.22.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/kr/pretty v0.1.0 // indirect + github.com/pion/transport/v2 v2.2.4 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/sys v0.18.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) From edaa25e4094f6b9551e2ed53f5cc1cd8a6dadc79 Mon Sep 17 00:00:00 2001 From: Stephan Rotolante Date: Tue, 2 Apr 2024 23:17:51 -0400 Subject: [PATCH 026/114] Expose stunGatherTimeout in Agent struct (#668) --- agent.go | 1 + agent_config.go | 11 +++++++++++ gather.go | 9 ++------- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/agent.go b/agent.go index 1a8c897..6dfe4f6 100644 --- a/agent.go +++ b/agent.go @@ -69,6 +69,7 @@ type Agent struct { srflxAcceptanceMinWait time.Duration prflxAcceptanceMinWait time.Duration relayAcceptanceMinWait time.Duration + stunGatherTimeout time.Duration tcpPriorityOffset uint16 disableActiveTCP bool diff --git a/agent_config.go b/agent_config.go index 6877313..73ebf8d 100644 --- a/agent_config.go +++ b/agent_config.go @@ -38,6 +38,9 @@ const ( // defaultRelayAcceptanceMinWait is the wait time before nominating a relay candidate defaultRelayAcceptanceMinWait = 2000 * time.Millisecond + // defaultStunGatherTimeout is the wait time for STUN responses + defaultStunGatherTimeout = 5 * time.Second + // defaultMaxBindingRequests is the maximum number of binding requests before considering a pair failed defaultMaxBindingRequests = 7 @@ -136,6 +139,8 @@ type AgentConfig struct { PrflxAcceptanceMinWait *time.Duration // HostAcceptanceMinWait specify a minimum wait time before selecting relay candidates RelayAcceptanceMinWait *time.Duration + // StunGatherTimeout specify a minimum wait time for STUN responses + StunGatherTimeout *time.Duration // Net is the our abstracted network interface for internal development purpose only // (see https://github.com/pion/transport) @@ -222,6 +227,12 @@ func (config *AgentConfig) initWithDefaults(a *Agent) { a.relayAcceptanceMinWait = *config.RelayAcceptanceMinWait } + if config.StunGatherTimeout == nil { + a.stunGatherTimeout = defaultStunGatherTimeout + } else { + a.stunGatherTimeout = *config.StunGatherTimeout + } + if config.TCPPriorityOffset == nil { a.tcpPriorityOffset = defaultTCPPriorityOffset } else { diff --git a/gather.go b/gather.go index fe56cc7..e97fe43 100644 --- a/gather.go +++ b/gather.go @@ -12,7 +12,6 @@ import ( "net/netip" "reflect" "sync" - "time" "github.com/pion/dtls/v2" "github.com/pion/ice/v3/internal/fakenet" @@ -22,10 +21,6 @@ import ( "github.com/pion/turn/v3" ) -const ( - stunGatherTimeout = time.Second * 5 -) - // Close a net.Conn and log if we have a failure func closeConnAndLog(c io.Closer, log logging.LeveledLogger, msg string, args ...interface{}) { if c == nil || (reflect.ValueOf(c).Kind() == reflect.Ptr && reflect.ValueOf(c).IsNil()) { @@ -479,7 +474,7 @@ func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.UR return } - xorAddr, err := a.udpMuxSrflx.GetXORMappedAddr(serverAddr, stunGatherTimeout) + xorAddr, err := a.udpMuxSrflx.GetXORMappedAddr(serverAddr, a.stunGatherTimeout) if err != nil { a.log.Warnf("Failed get server reflexive address %s %s: %v", network, url, err) return @@ -564,7 +559,7 @@ func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, net } }() - xorAddr, err := stunx.GetXORMappedAddr(conn, serverAddr, stunGatherTimeout) + xorAddr, err := stunx.GetXORMappedAddr(conn, serverAddr, a.stunGatherTimeout) if err != nil { closeConnAndLog(conn, a.log, "failed to get server reflexive address %s %s: %v", network, url, err) return From c1e4dd11e90fca600efb42b91ca9dc72de7c01db Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Wed, 3 Apr 2024 09:49:49 -0400 Subject: [PATCH 027/114] `Stun` -> `STUN` for Config Entry --- agent_config.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/agent_config.go b/agent_config.go index 73ebf8d..d1f2d0a 100644 --- a/agent_config.go +++ b/agent_config.go @@ -38,8 +38,8 @@ const ( // defaultRelayAcceptanceMinWait is the wait time before nominating a relay candidate defaultRelayAcceptanceMinWait = 2000 * time.Millisecond - // defaultStunGatherTimeout is the wait time for STUN responses - defaultStunGatherTimeout = 5 * time.Second + // defaultSTUNGatherTimeout is the wait time for STUN responses + defaultSTUNGatherTimeout = 5 * time.Second // defaultMaxBindingRequests is the maximum number of binding requests before considering a pair failed defaultMaxBindingRequests = 7 @@ -139,8 +139,8 @@ type AgentConfig struct { PrflxAcceptanceMinWait *time.Duration // HostAcceptanceMinWait specify a minimum wait time before selecting relay candidates RelayAcceptanceMinWait *time.Duration - // StunGatherTimeout specify a minimum wait time for STUN responses - StunGatherTimeout *time.Duration + // STUNGatherTimeout specify a minimum wait time for STUN responses + STUNGatherTimeout *time.Duration // Net is the our abstracted network interface for internal development purpose only // (see https://github.com/pion/transport) @@ -227,10 +227,10 @@ func (config *AgentConfig) initWithDefaults(a *Agent) { a.relayAcceptanceMinWait = *config.RelayAcceptanceMinWait } - if config.StunGatherTimeout == nil { - a.stunGatherTimeout = defaultStunGatherTimeout + if config.STUNGatherTimeout == nil { + a.stunGatherTimeout = defaultSTUNGatherTimeout } else { - a.stunGatherTimeout = *config.StunGatherTimeout + a.stunGatherTimeout = *config.STUNGatherTimeout } if config.TCPPriorityOffset == nil { From e253b36e6d6accb7d7b70752dc9568393f663d71 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 Apr 2024 19:21:18 +0000 Subject: [PATCH 028/114] Update module github.com/pion/turn/v3 to v3.0.2 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ecaf7c5..ed2fd5c 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/pion/randutil v0.1.0 github.com/pion/stun/v2 v2.0.0 github.com/pion/transport/v3 v3.0.2 - github.com/pion/turn/v3 v3.0.1 + github.com/pion/turn/v3 v3.0.2 github.com/stretchr/testify v1.9.0 golang.org/x/net v0.22.0 ) diff --git a/go.sum b/go.sum index 0188f17..7c2fe81 100644 --- a/go.sum +++ b/go.sum @@ -25,8 +25,8 @@ github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLh github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4= github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0= -github.com/pion/turn/v3 v3.0.1 h1:wLi7BTQr6/Q20R0vt/lHbjv6y4GChFtC33nkYbasoT8= -github.com/pion/turn/v3 v3.0.1/go.mod h1:MrJDKgqryDyWy1/4NT9TWfXWGMC7UHT6pJIv1+gMeNE= +github.com/pion/turn/v3 v3.0.2 h1:iBonAIIKRwkVUJBFiFd/kSjytP7FlX0HwCyBDJPRDdU= +github.com/pion/turn/v3 v3.0.2/go.mod h1:vw0Dz420q7VYAF3J4wJKzReLHIo2LGp4ev8nXQexYsc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= From fd4b1f8b1dccd83921af6be73094e2348f6c74c4 Mon Sep 17 00:00:00 2001 From: Pion <59523206+pionbot@users.noreply.github.com> Date: Tue, 9 Apr 2024 03:09:13 +0000 Subject: [PATCH 029/114] Update CI configs to v0.11.12 Update lint scripts and CI configs. --- .github/workflows/test.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index ad6eb90..08e4272 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -27,6 +27,7 @@ jobs: fail-fast: false with: go-version: ${{ matrix.go }} + secrets: inherit test-i386: uses: pion/.goassets/.github/workflows/test-i386.reusable.yml@master @@ -41,3 +42,4 @@ jobs: uses: pion/.goassets/.github/workflows/test-wasm.reusable.yml@master with: go-version: "1.22" # auto-update/latest-go-version + secrets: inherit From 0860817da641f4c40b78770068ed7a1fe437138b Mon Sep 17 00:00:00 2001 From: Christian Stewart Date: Wed, 10 Apr 2024 04:17:16 -0700 Subject: [PATCH 030/114] Rename to utils_test to drop stretchr/testify dep I noticed that the pion-ice package had ~1MB of extra binary size due to the dependency on stretchr/testify. Renaming test_utils.go to utils_test.go removes stretchr/testify from any non-test build and fixes this dependency. The tests still pass and the package still builds the same. One can check the dependency graph with goda: https://github.com/loov/goda goda graph "reach(.:all, github.com/stretchr/testify/require)" |\ dot -Tsvg -o graph.svg Signed-off-by: Christian Stewart --- test_utils.go => utils_test.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test_utils.go => utils_test.go (100%) diff --git a/test_utils.go b/utils_test.go similarity index 100% rename from test_utils.go rename to utils_test.go From 00621672bdd494003884e87999796546c4ea632c Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Sat, 13 Apr 2024 13:17:50 -0700 Subject: [PATCH 031/114] Reuse connectivity check ticker (#676) --- agent.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/agent.go b/agent.go index 6dfe4f6..84f7b42 100644 --- a/agent.go +++ b/agent.go @@ -8,6 +8,7 @@ package ice import ( "context" "fmt" + "math" "net" "net/netip" "strings" @@ -372,6 +373,9 @@ func (a *Agent) connectivityChecks() { } } + t := time.NewTimer(math.MaxInt64) + t.Stop() + for { interval := defaultKeepaliveInterval @@ -392,7 +396,8 @@ func (a *Agent) connectivityChecks() { updateInterval(a.disconnectedTimeout) updateInterval(a.failedTimeout) - t := time.NewTimer(interval) + t.Reset(interval) + select { case <-a.forceCandidateContact: t.Stop() From 6f743e393f66e55c32804276b24e1130a3a70add Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Mon, 15 Apr 2024 19:52:48 -0700 Subject: [PATCH 032/114] Handle timer stop race in agent connectivity check (#677) --- agent.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/agent.go b/agent.go index 84f7b42..e329e24 100644 --- a/agent.go +++ b/agent.go @@ -400,7 +400,9 @@ func (a *Agent) connectivityChecks() { select { case <-a.forceCandidateContact: - t.Stop() + if !t.Stop() { + <-t.C + } contact() case <-t.C: contact() From ad0240747d7a83c524a88b592ea8a41020292147 Mon Sep 17 00:00:00 2001 From: Kristian Paradis Date: Mon, 15 Apr 2024 13:58:34 -0400 Subject: [PATCH 033/114] Improve STUN resolution error message Add network to resolve stun host error message Most error message in gatherCandidatesSrflx and gatherCandidatesSrflxUDPMux include the network in them but not the one when failing to Resolve the host. --- gather.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gather.go b/gather.go index e97fe43..ec6831c 100644 --- a/gather.go +++ b/gather.go @@ -465,7 +465,7 @@ func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.UR hostPort := fmt.Sprintf("%s:%d", url.Host, url.Port) serverAddr, err := a.net.ResolveUDPAddr(network, hostPort) if err != nil { - a.log.Debugf("Failed to resolve STUN host: %s: %v", hostPort, err) + a.log.Debugf("Failed to resolve STUN host: %s %s: %v", network, hostPort, err) return } @@ -532,7 +532,7 @@ func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, net hostPort := fmt.Sprintf("%s:%d", url.Host, url.Port) serverAddr, err := a.net.ResolveUDPAddr(network, hostPort) if err != nil { - a.log.Debugf("Failed to resolve STUN host: %s: %v", hostPort, err) + a.log.Debugf("Failed to resolve STUN host: %s %s: %v", network, hostPort, err) return } From e6b8683f5db6d2ecb67c664c0d85354e90007a24 Mon Sep 17 00:00:00 2001 From: Federico Guerinoni Date: Tue, 16 Apr 2024 22:34:26 +0200 Subject: [PATCH 034/114] Fix doc comments about AgentConfig --- agent_config.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agent_config.go b/agent_config.go index d1f2d0a..5edbea4 100644 --- a/agent_config.go +++ b/agent_config.go @@ -133,11 +133,11 @@ type AgentConfig struct { // HostAcceptanceMinWait specify a minimum wait time before selecting host candidates HostAcceptanceMinWait *time.Duration - // HostAcceptanceMinWait specify a minimum wait time before selecting srflx candidates + // SrflxAcceptanceMinWait specify a minimum wait time before selecting srflx candidates SrflxAcceptanceMinWait *time.Duration - // HostAcceptanceMinWait specify a minimum wait time before selecting prflx candidates + // PrflxAcceptanceMinWait specify a minimum wait time before selecting prflx candidates PrflxAcceptanceMinWait *time.Duration - // HostAcceptanceMinWait specify a minimum wait time before selecting relay candidates + // RelayAcceptanceMinWait specify a minimum wait time before selecting relay candidates RelayAcceptanceMinWait *time.Duration // STUNGatherTimeout specify a minimum wait time for STUN responses STUNGatherTimeout *time.Duration From 75092df5e49970b997543749baa18bd2ff26cfff Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:36:20 +0000 Subject: [PATCH 035/114] Update module golang.org/x/net to v0.23.0 [SECURITY] Generated by renovateBot --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index ed2fd5c..acb2e56 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pion/transport/v3 v3.0.2 github.com/pion/turn/v3 v3.0.2 github.com/stretchr/testify v1.9.0 - golang.org/x/net v0.22.0 + golang.org/x/net v0.23.0 ) require ( diff --git a/go.sum b/go.sum index 7c2fe81..92e3205 100644 --- a/go.sum +++ b/go.sum @@ -59,8 +59,9 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From 82b41d98080c9900acb8b4426ba8a19e3c8223f2 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Fri, 19 Apr 2024 21:45:43 -0700 Subject: [PATCH 036/114] Prevent allocation from encoding UDP address (#684) --- udp_mux_test.go | 14 ++++++++++++++ udp_muxed_conn.go | 26 ++++++++++++++++++-------- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/udp_mux_test.go b/udp_mux_test.go index 8b64f65..5fdb67a 100644 --- a/udp_mux_test.go +++ b/udp_mux_test.go @@ -166,6 +166,20 @@ func TestAddressEncoding(t *testing.T) { } } +func BenchmarkAddressEncoding(b *testing.B) { + addr := &net.UDPAddr{ + IP: net.ParseIP("127.0.0.1"), + Port: 1234, + } + buf := make([]byte, 64) + + for i := 0; i < b.N; i++ { + if _, err := encodeUDPAddr(addr, buf); err != nil { + require.NoError(b, err) + } + } +} + func testMuxConnection(t *testing.T, udpMux *UDPMuxDefault, ufrag string, network string) { pktConn, err := udpMux.GetConn(ufrag, udpMux.LocalAddr()) require.NoError(t, err, "error retrieving muxed connection for ufrag") diff --git a/udp_muxed_conn.go b/udp_muxed_conn.go index 0244d13..88c1a53 100644 --- a/udp_muxed_conn.go +++ b/udp_muxed_conn.go @@ -7,8 +7,11 @@ import ( "encoding/binary" "io" "net" + "net/netip" + "reflect" "sync" "time" + "unsafe" "github.com/pion/logging" "github.com/pion/transport/v3/packetio" @@ -211,19 +214,26 @@ func (c *udpMuxedConn) writePacket(data []byte, addr *net.UDPAddr) error { } func encodeUDPAddr(addr *net.UDPAddr, buf []byte) (int, error) { - ipData, err := addr.IP.MarshalText() - if err != nil { - return 0, err + if len(addr.IP) != 0 && len(addr.IP) != net.IPv4len && len(addr.IP) != net.IPv6len { + return 0, errInvalidAddress } - total := 2 + len(ipData) + 2 + len(addr.Zone) + + var n int + if ip4 := addr.IP.To4(); len(ip4) == net.IPv4len { + d := (*reflect.SliceHeader)(unsafe.Pointer(&ip4)) // nolint:gosec + n = len(netip.AddrFrom4(*(*[4]byte)(unsafe.Pointer(d.Data))).AppendTo(buf[2:2])) // nolint:gosec + } else if len(addr.IP) != 0 { + d := (*reflect.SliceHeader)(unsafe.Pointer(&addr.IP)) // nolint:gosec + n = len(netip.AddrFrom16(*(*[16]byte)(unsafe.Pointer(d.Data))).AppendTo(buf[2:2])) // nolint:gosec + } + + total := 2 + n + 2 + len(addr.Zone) if total > len(buf) { return 0, io.ErrShortBuffer } - binary.LittleEndian.PutUint16(buf, uint16(len(ipData))) - offset := 2 - n := copy(buf[offset:], ipData) - offset += n + binary.LittleEndian.PutUint16(buf, uint16(n)) + offset := 2 + n binary.LittleEndian.PutUint16(buf[offset:], uint16(addr.Port)) offset += 2 copy(buf[offset:], addr.Zone) From 7263f68acd211dbe04bf34adaa9f9453f01ebd65 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Apr 2024 04:46:17 +0000 Subject: [PATCH 037/114] Update module github.com/pion/turn/v3 to v3.0.3 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index acb2e56..c169d45 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/pion/randutil v0.1.0 github.com/pion/stun/v2 v2.0.0 github.com/pion/transport/v3 v3.0.2 - github.com/pion/turn/v3 v3.0.2 + github.com/pion/turn/v3 v3.0.3 github.com/stretchr/testify v1.9.0 golang.org/x/net v0.23.0 ) diff --git a/go.sum b/go.sum index 92e3205..c0aefc3 100644 --- a/go.sum +++ b/go.sum @@ -25,8 +25,8 @@ github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLh github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4= github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0= -github.com/pion/turn/v3 v3.0.2 h1:iBonAIIKRwkVUJBFiFd/kSjytP7FlX0HwCyBDJPRDdU= -github.com/pion/turn/v3 v3.0.2/go.mod h1:vw0Dz420q7VYAF3J4wJKzReLHIo2LGp4ev8nXQexYsc= +github.com/pion/turn/v3 v3.0.3 h1:1e3GVk8gHZLPBA5LqadWYV60lmaKUaHCkm9DX9CkGcE= +github.com/pion/turn/v3 v3.0.3/go.mod h1:vw0Dz420q7VYAF3J4wJKzReLHIo2LGp4ev8nXQexYsc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= From c1b438658d4ef84fe82551fd1c3f566731b5c436 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Sun, 21 Apr 2024 00:06:11 -0700 Subject: [PATCH 038/114] Reduce allocation in udp muxed conn addr decode (#686) --- udp_mux_test.go | 22 ++++++++++++++--- udp_muxed_conn.go | 62 +++++++++++++++++++++-------------------------- 2 files changed, 45 insertions(+), 39 deletions(-) diff --git a/udp_mux_test.go b/udp_mux_test.go index 5fdb67a..77c575f 100644 --- a/udp_mux_test.go +++ b/udp_mux_test.go @@ -173,11 +173,25 @@ func BenchmarkAddressEncoding(b *testing.B) { } buf := make([]byte, 64) - for i := 0; i < b.N; i++ { - if _, err := encodeUDPAddr(addr, buf); err != nil { - require.NoError(b, err) + b.Run("encode", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := encodeUDPAddr(addr, buf); err != nil { + require.NoError(b, err) + } } - } + }) + + b.Run("decode", func(b *testing.B) { + n, _ := encodeUDPAddr(addr, buf) + var addr *net.UDPAddr + var err error + for i := 0; i < b.N; i++ { + if addr, err = decodeUDPAddr(buf[:n]); err != nil { + require.NoError(b, err) + } + } + _ = addr + }) } func testMuxConnection(t *testing.T, udpMux *UDPMuxDefault, ufrag string, network string) { diff --git a/udp_muxed_conn.go b/udp_muxed_conn.go index 88c1a53..9c279b8 100644 --- a/udp_muxed_conn.go +++ b/udp_muxed_conn.go @@ -7,11 +7,8 @@ import ( "encoding/binary" "io" "net" - "net/netip" - "reflect" "sync" "time" - "unsafe" "github.com/pion/logging" "github.com/pion/transport/v3/packetio" @@ -214,51 +211,46 @@ func (c *udpMuxedConn) writePacket(data []byte, addr *net.UDPAddr) error { } func encodeUDPAddr(addr *net.UDPAddr, buf []byte) (int, error) { - if len(addr.IP) != 0 && len(addr.IP) != net.IPv4len && len(addr.IP) != net.IPv6len { - return 0, errInvalidAddress - } - - var n int - if ip4 := addr.IP.To4(); len(ip4) == net.IPv4len { - d := (*reflect.SliceHeader)(unsafe.Pointer(&ip4)) // nolint:gosec - n = len(netip.AddrFrom4(*(*[4]byte)(unsafe.Pointer(d.Data))).AppendTo(buf[2:2])) // nolint:gosec - } else if len(addr.IP) != 0 { - d := (*reflect.SliceHeader)(unsafe.Pointer(&addr.IP)) // nolint:gosec - n = len(netip.AddrFrom16(*(*[16]byte)(unsafe.Pointer(d.Data))).AppendTo(buf[2:2])) // nolint:gosec - } - - total := 2 + n + 2 + len(addr.Zone) - if total > len(buf) { + total := 1 + len(addr.IP) + 2 + len(addr.Zone) + if len(buf) < total { return 0, io.ErrShortBuffer } - binary.LittleEndian.PutUint16(buf, uint16(n)) - offset := 2 + n + buf[0] = uint8(len(addr.IP)) + offset := 1 + + copy(buf[offset:], addr.IP) + offset += len(addr.IP) + binary.LittleEndian.PutUint16(buf[offset:], uint16(addr.Port)) offset += 2 + copy(buf[offset:], addr.Zone) return total, nil } func decodeUDPAddr(buf []byte) (*net.UDPAddr, error) { - addr := net.UDPAddr{} + addr := &net.UDPAddr{} - offset := 0 - ipLen := int(binary.LittleEndian.Uint16(buf[:2])) - offset += 2 // Basic bounds checking - if ipLen+offset > len(buf) { + if len(buf) == 0 || len(buf) < int(buf[0])+3 { return nil, io.ErrShortBuffer } - if err := addr.IP.UnmarshalText(buf[offset : offset+ipLen]); err != nil { - return nil, err - } - offset += ipLen - addr.Port = int(binary.LittleEndian.Uint16(buf[offset : offset+2])) - offset += 2 - zone := make([]byte, len(buf[offset:])) - copy(zone, buf[offset:]) - addr.Zone = string(zone) - return &addr, nil + ipLen := int(buf[0]) + offset := 1 + + if ipLen == 0 { + addr.IP = nil + } else { + addr.IP = append(addr.IP[:0], buf[offset:offset+ipLen]...) + offset += ipLen + } + + addr.Port = int(binary.LittleEndian.Uint16(buf[offset:])) + offset += 2 + + addr.Zone = string(buf[offset:]) + + return addr, nil } From a834f55f2d40f82c456883288aae7511fa14b5b9 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Sun, 21 Apr 2024 01:55:26 -0700 Subject: [PATCH 039/114] Remove pessimistic String calls for low level logs (#687) --- agent.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agent.go b/agent.go index e329e24..144f9c2 100644 --- a/agent.go +++ b/agent.go @@ -728,7 +728,7 @@ func (a *Agent) addCandidate(ctx context.Context, c Candidate, candidateConn net set := a.localCandidates[c.NetworkType()] for _, candidate := range set { if candidate.Equal(c) { - a.log.Debugf("Ignore duplicate candidate: %s", c.String()) + a.log.Debugf("Ignore duplicate candidate: %s", c) if err := c.close(); err != nil { a.log.Warnf("Failed to close duplicate candidate: %v", err) } @@ -886,7 +886,7 @@ func (a *Agent) findRemoteCandidate(networkType NetworkType, addr net.Addr) Cand } func (a *Agent) sendBindingRequest(m *stun.Message, local, remote Candidate) { - a.log.Tracef("Ping STUN from %s to %s", local.String(), remote.String()) + a.log.Tracef("Ping STUN from %s to %s", local, remote) a.invalidatePendingBindingRequests(time.Now()) a.pendingBindingRequests = append(a.pendingBindingRequests, bindingRequest{ @@ -1001,7 +1001,7 @@ func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr) a.selector.HandleSuccessResponse(m, local, remoteCandidate, remote) } else if m.Type.Class == stun.ClassRequest { - a.log.Tracef("Inbound STUN (Request) from %s to %s, useCandidate: %v", remote.String(), local.String(), m.Contains(stun.AttrUseCandidate)) + a.log.Tracef("Inbound STUN (Request) from %s to %s, useCandidate: %v", remote, local, m.Contains(stun.AttrUseCandidate)) if err = stunx.AssertUsername(m, a.localUfrag+":"+a.remoteUfrag); err != nil { a.log.Warnf("Discard message from (%s), %v", remote, err) From 75ca3a2c3ad11270f892f49210e37fca168d19d7 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Tue, 30 Apr 2024 03:23:43 -0700 Subject: [PATCH 040/114] Skip UDP address serialization in muxed conn (#688) --- udp_mux.go | 13 ++- udp_mux_test.go | 68 --------------- udp_muxed_conn.go | 217 ++++++++++++++++++++++------------------------ 3 files changed, 111 insertions(+), 187 deletions(-) diff --git a/udp_mux.go b/udp_mux.go index 40e8ad9..f504259 100644 --- a/udp_mux.go +++ b/udp_mux.go @@ -48,8 +48,6 @@ type UDPMuxDefault struct { localAddrsForUnspecified []net.Addr } -const maxAddrSize = 512 - // UDPMuxParams are parameters for UDPMux. type UDPMuxParams struct { Logger logging.LeveledLogger @@ -120,7 +118,7 @@ func NewUDPMuxDefault(params UDPMuxParams) *UDPMuxDefault { pool: &sync.Pool{ New: func() interface{} { // Big enough buffer to fit both packet and address - return newBufferHolder(receiveMTU + maxAddrSize) + return newBufferHolder(receiveMTU) }, }, localAddrsForUnspecified: localAddrsForUnspecified, @@ -365,7 +363,9 @@ func (m *UDPMuxDefault) getConn(ufrag string, isIPv6 bool) (val *udpMuxedConn, o } type bufferHolder struct { - buf []byte + next *bufferHolder + buf []byte + addr *net.UDPAddr } func newBufferHolder(size int) *bufferHolder { @@ -374,6 +374,11 @@ func newBufferHolder(size int) *bufferHolder { } } +func (b *bufferHolder) reset() { + b.next = nil + b.addr = nil +} + type ipPort struct { addr netip.Addr port uint16 diff --git a/udp_mux_test.go b/udp_mux_test.go index 77c575f..d25a070 100644 --- a/udp_mux_test.go +++ b/udp_mux_test.go @@ -126,74 +126,6 @@ func TestUDPMux(t *testing.T) { } } -func TestAddressEncoding(t *testing.T) { - cases := []struct { - name string - addr net.UDPAddr - }{ - { - name: "empty address", - }, - { - name: "ipv4", - addr: net.UDPAddr{ - IP: net.IPv4(244, 120, 0, 5), - Port: 6000, - Zone: "", - }, - }, - { - name: "ipv6", - addr: net.UDPAddr{ - IP: net.IPv6loopback, - Port: 2500, - Zone: "zone", - }, - }, - } - - for _, c := range cases { - addr := c.addr - t.Run(c.name, func(t *testing.T) { - buf := make([]byte, maxAddrSize) - n, err := encodeUDPAddr(&addr, buf) - require.NoError(t, err) - - parsedAddr, err := decodeUDPAddr(buf[:n]) - require.NoError(t, err) - require.EqualValues(t, &addr, parsedAddr) - }) - } -} - -func BenchmarkAddressEncoding(b *testing.B) { - addr := &net.UDPAddr{ - IP: net.ParseIP("127.0.0.1"), - Port: 1234, - } - buf := make([]byte, 64) - - b.Run("encode", func(b *testing.B) { - for i := 0; i < b.N; i++ { - if _, err := encodeUDPAddr(addr, buf); err != nil { - require.NoError(b, err) - } - } - }) - - b.Run("decode", func(b *testing.B) { - n, _ := encodeUDPAddr(addr, buf) - var addr *net.UDPAddr - var err error - for i := 0; i < b.N; i++ { - if addr, err = decodeUDPAddr(buf[:n]); err != nil { - require.NoError(b, err) - } - } - _ = addr - }) -} - func testMuxConnection(t *testing.T, udpMux *UDPMuxDefault, ufrag string, network string) { pktConn, err := udpMux.GetConn(ufrag, udpMux.LocalAddr()) require.NoError(t, err, "error retrieving muxed connection for ufrag") diff --git a/udp_muxed_conn.go b/udp_muxed_conn.go index 9c279b8..4e6588d 100644 --- a/udp_muxed_conn.go +++ b/udp_muxed_conn.go @@ -4,14 +4,20 @@ package ice import ( - "encoding/binary" "io" "net" "sync" "time" "github.com/pion/logging" - "github.com/pion/transport/v3/packetio" +) + +type udpMuxedConnState int + +const ( + udpMuxedConnOpen udpMuxedConnState = iota + udpMuxedConnWaiting + udpMuxedConnClosed ) type udpMuxedConnParams struct { @@ -28,52 +34,61 @@ type udpMuxedConn struct { // Remote addresses that we have sent to on this conn addresses []ipPort - // Channel holding incoming packets - buf *packetio.Buffer - closedChan chan struct{} - closeOnce sync.Once - mu sync.Mutex + // FIFO queue holding incoming packets + bufHead, bufTail *bufferHolder + notify chan struct{} + closedChan chan struct{} + state udpMuxedConnState + mu sync.Mutex } func newUDPMuxedConn(params *udpMuxedConnParams) *udpMuxedConn { - p := &udpMuxedConn{ + return &udpMuxedConn{ params: params, - buf: packetio.NewBuffer(), + notify: make(chan struct{}, 1), closedChan: make(chan struct{}), } - - return p } func (c *udpMuxedConn) ReadFrom(b []byte) (n int, rAddr net.Addr, err error) { - buf := c.params.AddrPool.Get().(*bufferHolder) //nolint:forcetypeassert - defer c.params.AddrPool.Put(buf) + for { + c.mu.Lock() + if c.bufTail != nil { + pkt := c.bufTail + c.bufTail = pkt.next - // Read address - total, err := c.buf.Read(buf.buf) - if err != nil { - return 0, nil, err + if pkt == c.bufHead { + c.bufHead = nil + } + c.mu.Unlock() + + if len(b) < len(pkt.buf) { + err = io.ErrShortBuffer + } else { + n = copy(b, pkt.buf) + rAddr = pkt.addr + } + + pkt.reset() + c.params.AddrPool.Put(pkt) + + return + } + + if c.state == udpMuxedConnClosed { + c.mu.Unlock() + return 0, nil, io.EOF + } + + c.state = udpMuxedConnWaiting + c.mu.Unlock() + + select { + case <-c.notify: + case <-c.closedChan: + return 0, nil, io.EOF + } } - - dataLen := int(binary.LittleEndian.Uint16(buf.buf[:2])) - if dataLen > total || dataLen > len(b) { - return 0, nil, io.ErrShortBuffer - } - - // Read data and then address - offset := 2 - copy(b, buf.buf[offset:offset+dataLen]) - offset += dataLen - - // Read address len & decode address - addrLen := int(binary.LittleEndian.Uint16(buf.buf[offset : offset+2])) - offset += 2 - - if rAddr, err = decodeUDPAddr(buf.buf[offset : offset+addrLen]); err != nil { - return 0, nil, err - } - - return dataLen, rAddr, nil } func (c *udpMuxedConn) WriteTo(buf []byte, rAddr net.Addr) (n int, err error) { @@ -118,21 +133,28 @@ func (c *udpMuxedConn) CloseChannel() <-chan struct{} { } func (c *udpMuxedConn) Close() error { - var err error - c.closeOnce.Do(func() { - err = c.buf.Close() + c.mu.Lock() + defer c.mu.Unlock() + if c.state != udpMuxedConnClosed { + for pkt := c.bufTail; pkt != nil; { + next := pkt.next + + pkt.reset() + c.params.AddrPool.Put(pkt) + + pkt = next + } + + c.state = udpMuxedConnClosed close(c.closedChan) - }) - return err + } + return nil } func (c *udpMuxedConn) isClosed() bool { - select { - case <-c.closedChan: - return true - default: - return false - } + c.mu.Lock() + defer c.mu.Unlock() + return c.state == udpMuxedConnClosed } func (c *udpMuxedConn) getAddresses() []ipPort { @@ -178,79 +200,44 @@ func (c *udpMuxedConn) containsAddress(addr ipPort) bool { } func (c *udpMuxedConn) writePacket(data []byte, addr *net.UDPAddr) error { - // Write two packets, address and data - buf := c.params.AddrPool.Get().(*bufferHolder) //nolint:forcetypeassert - defer c.params.AddrPool.Put(buf) - - // Format of buffer | data len | data bytes | addr len | addr bytes | - if len(buf.buf) < len(data)+maxAddrSize { + pkt := c.params.AddrPool.Get().(*bufferHolder) //nolint:forcetypeassert + if cap(pkt.buf) < len(data) { + c.params.AddrPool.Put(pkt) return io.ErrShortBuffer } - // Data length - binary.LittleEndian.PutUint16(buf.buf, uint16(len(data))) - offset := 2 - // Data - copy(buf.buf[offset:], data) - offset += len(data) + pkt.buf = append(pkt.buf[:0], data...) + pkt.addr = addr - // Write address first, leaving room for its length - n, err := encodeUDPAddr(addr, buf.buf[offset+2:]) - if err != nil { - return err + c.mu.Lock() + if c.state == udpMuxedConnClosed { + c.mu.Unlock() + + pkt.reset() + c.params.AddrPool.Put(pkt) + + return io.ErrClosedPipe } - total := offset + n + 2 - // Address len - binary.LittleEndian.PutUint16(buf.buf[offset:], uint16(n)) - - if _, err := c.buf.Write(buf.buf[:total]); err != nil { - return err + if c.bufHead != nil { + c.bufHead.next = pkt } + c.bufHead = pkt + + if c.bufTail == nil { + c.bufTail = pkt + } + + state := c.state + c.state = udpMuxedConnOpen + c.mu.Unlock() + + if state == udpMuxedConnWaiting { + select { + case c.notify <- struct{}{}: + default: + } + } + return nil } - -func encodeUDPAddr(addr *net.UDPAddr, buf []byte) (int, error) { - total := 1 + len(addr.IP) + 2 + len(addr.Zone) - if len(buf) < total { - return 0, io.ErrShortBuffer - } - - buf[0] = uint8(len(addr.IP)) - offset := 1 - - copy(buf[offset:], addr.IP) - offset += len(addr.IP) - - binary.LittleEndian.PutUint16(buf[offset:], uint16(addr.Port)) - offset += 2 - - copy(buf[offset:], addr.Zone) - return total, nil -} - -func decodeUDPAddr(buf []byte) (*net.UDPAddr, error) { - addr := &net.UDPAddr{} - - // Basic bounds checking - if len(buf) == 0 || len(buf) < int(buf[0])+3 { - return nil, io.ErrShortBuffer - } - - ipLen := int(buf[0]) - offset := 1 - - if ipLen == 0 { - addr.IP = nil - } else { - addr.IP = append(addr.IP[:0], buf[offset:offset+ipLen]...) - offset += ipLen - } - - addr.Port = int(binary.LittleEndian.Uint16(buf[offset:])) - offset += 2 - - addr.Zone = string(buf[offset:]) - - return addr, nil -} From d30f13f235b22e46e70a6b57c28a4256545f4729 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Tue, 30 Apr 2024 03:49:13 -0700 Subject: [PATCH 041/114] Return io.ErrClosedPipe from closed udpMuxedConn --- udp_muxed_conn.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/udp_muxed_conn.go b/udp_muxed_conn.go index 4e6588d..ba3297b 100644 --- a/udp_muxed_conn.go +++ b/udp_muxed_conn.go @@ -77,7 +77,7 @@ func (c *udpMuxedConn) ReadFrom(b []byte) (n int, rAddr net.Addr, err error) { if c.state == udpMuxedConnClosed { c.mu.Unlock() - return 0, nil, io.EOF + return 0, nil, io.ErrClosedPipe } c.state = udpMuxedConnWaiting @@ -86,7 +86,7 @@ func (c *udpMuxedConn) ReadFrom(b []byte) (n int, rAddr net.Addr, err error) { select { case <-c.notify: case <-c.closedChan: - return 0, nil, io.EOF + return 0, nil, io.ErrClosedPipe } } } From edb69295c04429931ede6a3df01d49520a1b31dc Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Tue, 30 Apr 2024 06:01:33 -0700 Subject: [PATCH 042/114] Avoid allocation storing last active time --- candidate_base.go | 16 ++++++++-------- candidate_test.go | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/candidate_base.go b/candidate_base.go index 1a60899..81b54a5 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -31,8 +31,8 @@ type candidateBase struct { resolvedAddr net.Addr - lastSent atomic.Value - lastReceived atomic.Value + lastSent atomic.Int64 + lastReceived atomic.Int64 conn net.PacketConn currAgent *Agent @@ -409,27 +409,27 @@ func (c *candidateBase) String() string { // LastReceived returns a time.Time indicating the last time // this candidate was received func (c *candidateBase) LastReceived() time.Time { - if lastReceived, ok := c.lastReceived.Load().(time.Time); ok { - return lastReceived + if lastReceived := c.lastReceived.Load(); lastReceived != 0 { + return time.Unix(0, lastReceived) } return time.Time{} } func (c *candidateBase) setLastReceived(t time.Time) { - c.lastReceived.Store(t) + c.lastReceived.Store(t.UnixNano()) } // LastSent returns a time.Time indicating the last time // this candidate was sent func (c *candidateBase) LastSent() time.Time { - if lastSent, ok := c.lastSent.Load().(time.Time); ok { - return lastSent + if lastSent := c.lastSent.Load(); lastSent != 0 { + return time.Unix(0, lastSent) } return time.Time{} } func (c *candidateBase) setLastSent(t time.Time) { - c.lastSent.Store(t) + c.lastSent.Store(t.UnixNano()) } func (c *candidateBase) seen(outbound bool) { diff --git a/candidate_test.go b/candidate_test.go index 04bb17c..aecbeec 100644 --- a/candidate_test.go +++ b/candidate_test.go @@ -186,7 +186,7 @@ func TestCandidateLastSent(t *testing.T) { require.Equal(t, candidate.LastSent(), time.Time{}) now := time.Now() candidate.setLastSent(now) - require.Equal(t, candidate.LastSent(), now) + require.EqualValues(t, 0, now.Sub(candidate.LastSent())) } func TestCandidateLastReceived(t *testing.T) { @@ -194,7 +194,7 @@ func TestCandidateLastReceived(t *testing.T) { require.Equal(t, candidate.LastReceived(), time.Time{}) now := time.Now() candidate.setLastReceived(now) - require.Equal(t, candidate.LastReceived(), now) + require.EqualValues(t, 0, now.Sub(candidate.LastReceived())) } func TestCandidateFoundation(t *testing.T) { From ed41bbf83b84cf055293b48116e4be06cd0f2e53 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Tue, 30 Apr 2024 21:11:50 -0700 Subject: [PATCH 043/114] Return io.EOF from closed udpMuxedConn --- udp_muxed_conn.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/udp_muxed_conn.go b/udp_muxed_conn.go index ba3297b..4e6588d 100644 --- a/udp_muxed_conn.go +++ b/udp_muxed_conn.go @@ -77,7 +77,7 @@ func (c *udpMuxedConn) ReadFrom(b []byte) (n int, rAddr net.Addr, err error) { if c.state == udpMuxedConnClosed { c.mu.Unlock() - return 0, nil, io.ErrClosedPipe + return 0, nil, io.EOF } c.state = udpMuxedConnWaiting @@ -86,7 +86,7 @@ func (c *udpMuxedConn) ReadFrom(b []byte) (n int, rAddr net.Addr, err error) { select { case <-c.notify: case <-c.closedChan: - return 0, nil, io.ErrClosedPipe + return 0, nil, io.EOF } } } From 1d06841b4777b4746f465f7ce5cfa3b58d340b32 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Tue, 30 Apr 2024 22:27:00 -0700 Subject: [PATCH 044/114] Clean up packet queue in udpMuxedConn Close --- udp_muxed_conn.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/udp_muxed_conn.go b/udp_muxed_conn.go index 4e6588d..9f438b3 100644 --- a/udp_muxed_conn.go +++ b/udp_muxed_conn.go @@ -144,6 +144,8 @@ func (c *udpMuxedConn) Close() error { pkt = next } + c.bufHead = nil + c.bufTail = nil c.state = udpMuxedConnClosed close(c.closedChan) From 78a9ef9c57071323051bf6683ab917c638f86931 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 05:37:22 +0000 Subject: [PATCH 045/114] Update module golang.org/x/net to v0.24.0 Generated by renovateBot --- go.mod | 6 +++--- go.sum | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index c169d45..6cd02ba 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pion/transport/v3 v3.0.2 github.com/pion/turn/v3 v3.0.3 github.com/stretchr/testify v1.9.0 - golang.org/x/net v0.23.0 + golang.org/x/net v0.24.0 ) require ( @@ -20,8 +20,8 @@ require ( github.com/kr/pretty v0.1.0 // indirect github.com/pion/transport/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/sys v0.18.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/sys v0.19.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c0aefc3..01eae10 100644 --- a/go.sum +++ b/go.sum @@ -46,8 +46,9 @@ golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -60,8 +61,8 @@ golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -76,8 +77,9 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= From b5f4ca01aedc68df09dcbde3f7080766234fee5b Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Tue, 30 Apr 2024 23:53:09 -0700 Subject: [PATCH 046/114] Remove pessimistic String calls for low level logs --- selection.go | 11 +++++------ udp_mux.go | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/selection.go b/selection.go index e6e1fac..55ad20b 100644 --- a/selection.go +++ b/selection.go @@ -59,7 +59,7 @@ func (s *controllingSelector) ContactCandidates() { default: p := s.agent.getBestValidCandidatePair() if p != nil && s.isNominatable(p.Local) && s.isNominatable(p.Remote) { - s.log.Tracef("Nominatable pair found, nominating (%s, %s)", p.Local.String(), p.Remote.String()) + s.log.Tracef("Nominatable pair found, nominating (%s, %s)", p.Local, p.Remote) p.nominated = true s.nominatedPair = p s.nominatePair(p) @@ -87,7 +87,7 @@ func (s *controllingSelector) nominatePair(pair *CandidatePair) { return } - s.log.Tracef("Ping STUN (nominate candidate pair) from %s to %s", pair.Local.String(), pair.Remote.String()) + s.log.Tracef("Ping STUN (nominate candidate pair) from %s to %s", pair.Local, pair.Remote) s.agent.sendBindingRequest(msg, pair.Local, pair.Remote) } @@ -106,8 +106,7 @@ func (s *controllingSelector) HandleBindingRequest(m *stun.Message, local, remot if bestPair == nil { s.log.Tracef("No best pair available") } else if bestPair.equal(p) && s.isNominatable(p.Local) && s.isNominatable(p.Remote) { - s.log.Tracef("The candidate (%s, %s) is the best candidate available, marking it as nominated", - p.Local.String(), p.Remote.String()) + s.log.Tracef("The candidate (%s, %s) is the best candidate available, marking it as nominated", p.Local, p.Remote) s.nominatedPair = p s.nominatePair(p) } @@ -130,7 +129,7 @@ func (s *controllingSelector) HandleSuccessResponse(m *stun.Message, local, remo return } - s.log.Tracef("Inbound STUN (SuccessResponse) from %s to %s", remote.String(), local.String()) + s.log.Tracef("Inbound STUN (SuccessResponse) from %s to %s", remote, local) p := s.agent.findPair(local, remote) if p == nil { @@ -221,7 +220,7 @@ func (s *controlledSelector) HandleSuccessResponse(m *stun.Message, local, remot return } - s.log.Tracef("Inbound STUN (SuccessResponse) from %s to %s", remote.String(), local.String()) + s.log.Tracef("Inbound STUN (SuccessResponse) from %s to %s", remote, local) p := s.agent.findPair(local, remote) if p == nil { diff --git a/udp_mux.go b/udp_mux.go index f504259..46f81ca 100644 --- a/udp_mux.go +++ b/udp_mux.go @@ -343,7 +343,7 @@ func (m *UDPMuxDefault) connWorker() { } if destinationConn == nil { - m.params.Logger.Tracef("Dropping packet from %s, addr: %s", udpAddr.addr.String(), addr.String()) + m.params.Logger.Tracef("Dropping packet from %s, addr: %s", udpAddr.addr, addr) continue } From 07b74c8a970fae136e3abd4074a74657edfba82e Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Wed, 1 May 2024 16:39:40 -0400 Subject: [PATCH 047/114] Add BindingRequestHandler Allow the user to perform custom processing for inbound STUN Binding requests. This allows users to do some of the following * Log incoming Binding Requests for debugging * Implement draft-thatcher-ice-renomination * Implement custom CandidatePair switching logic Resolves pion/webrtc#2539 Resolves pion/webrtc#2585 Resolves #623 --- agent.go | 6 ++ agent_config.go | 7 +++ selection.go | 20 ++++-- selection_test.go | 156 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 184 insertions(+), 5 deletions(-) create mode 100644 selection_test.go diff --git a/agent.go b/agent.go index 144f9c2..761c07c 100644 --- a/agent.go +++ b/agent.go @@ -119,6 +119,10 @@ type Agent struct { // 1:1 D-NAT IP address mapping extIPMapper *externalIPMapper + // Callback that allows user to implement custom behavior + // for STUN Binding Requests + userBindingRequestHandler func(m *stun.Message, local, remote Candidate, pair *CandidatePair) bool + gatherCandidateCancel func() gatherCandidateDone chan struct{} @@ -213,6 +217,8 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit includeLoopback: config.IncludeLoopback, disableActiveTCP: config.DisableActiveTCP, + + userBindingRequestHandler: config.BindingRequestHandler, } a.connectionStateNotifier = &handlerNotifier{connectionStateFunc: a.onConnectionStateChange} a.candidateNotifier = &handlerNotifier{candidateFunc: a.onCandidate} diff --git a/agent_config.go b/agent_config.go index 5edbea4..4c87fa1 100644 --- a/agent_config.go +++ b/agent_config.go @@ -193,6 +193,13 @@ type AgentConfig struct { // DisableActiveTCP can be used to disable Active TCP candidates. Otherwise when TCP is enabled // Active TCP candidates will be created when a new passive TCP remote candidate is added. DisableActiveTCP bool + + // BindingRequestHandler allows applications to perform logic on incoming STUN Binding Requests + // This was implemented to allow users to + // * Log incoming Binding Requests for debugging + // * Implement draft-thatcher-ice-renomination + // * Implement custom CandidatePair switching logic + BindingRequestHandler func(m *stun.Message, local, remote Candidate, pair *CandidatePair) bool } // initWithDefaults populates an agent and falls back to defaults if fields are unset diff --git a/selection.go b/selection.go index 55ad20b..09a0988 100644 --- a/selection.go +++ b/selection.go @@ -111,6 +111,12 @@ func (s *controllingSelector) HandleBindingRequest(m *stun.Message, local, remot s.nominatePair(p) } } + + if s.agent.userBindingRequestHandler != nil { + if shouldSwitch := s.agent.userBindingRequestHandler(m, local, remote, p); shouldSwitch { + s.agent.setSelectedPair(p) + } + } } func (s *controllingSelector) HandleSuccessResponse(m *stun.Message, local, remote Candidate, remoteAddr net.Addr) { @@ -242,14 +248,12 @@ func (s *controlledSelector) HandleSuccessResponse(m *stun.Message, local, remot } func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote Candidate) { - useCandidate := m.Contains(stun.AttrUseCandidate) - p := s.agent.findPair(local, remote) if p == nil { p = s.agent.addPair(local, remote) } - if useCandidate { + if m.Contains(stun.AttrUseCandidate) { // https://tools.ietf.org/html/rfc8445#section-7.3.1.5 if p.state == CandidatePairStateSucceeded { @@ -257,8 +261,8 @@ func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote // previously sent by this pair produced a successful response and // generated a valid pair (Section 7.2.5.3.2). The agent sets the // nominated flag value of the valid pair to true. - if selectedPair := s.agent.getSelectedPair(); selectedPair == nil || - (selectedPair != p && selectedPair.priority() <= p.priority()) { + selectedPair := s.agent.getSelectedPair() + if selectedPair == nil || (selectedPair != p && selectedPair.priority() <= p.priority()) { s.agent.setSelectedPair(p) } else if selectedPair != p { s.log.Tracef("Ignore nominate new pair %s, already nominated pair %s", p, selectedPair) @@ -278,6 +282,12 @@ func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote s.agent.sendBindingSuccess(m, local, remote) s.PingCandidate(local, remote) + + if s.agent.userBindingRequestHandler != nil { + if shouldSwitch := s.agent.userBindingRequestHandler(m, local, remote, p); shouldSwitch { + s.agent.setSelectedPair(p) + } + } } type liteSelector struct { diff --git a/selection_test.go b/selection_test.go new file mode 100644 index 0000000..a5e17fc --- /dev/null +++ b/selection_test.go @@ -0,0 +1,156 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +//go:build !js +// +build !js + +package ice + +import ( + "bytes" + "context" + "errors" + "io" + "net" + "sync/atomic" + "testing" + "time" + + "github.com/pion/stun/v2" + "github.com/pion/transport/v3/test" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func sendUntilDone(t *testing.T, writingConn, readingConn net.Conn, maxAttempts int) bool { + testMessage := []byte("Hello World") + testBuffer := make([]byte, len(testMessage)) + + readDone, readDoneCancel := context.WithCancel(context.Background()) + go func() { + _, err := readingConn.Read(testBuffer) + if errors.Is(err, io.EOF) { + return + } + + require.NoError(t, err) + require.True(t, bytes.Equal(testMessage, testBuffer)) + + readDoneCancel() + }() + + attempts := 0 + for { + select { + case <-time.After(5 * time.Millisecond): + if attempts > maxAttempts { + return false + } + + _, err := writingConn.Write(testMessage) + require.NoError(t, err) + attempts++ + case <-readDone.Done(): + return true + } + } +} + +func TestBindingRequestHandler(t *testing.T) { + defer test.CheckRoutines(t)() + defer test.TimeOut(time.Second * 30).Stop() + + var switchToNewCandidatePair, controlledLoggingFired atomic.Value + oneHour := time.Hour + keepaliveInterval := time.Millisecond * 20 + + aNotifier, aConnected := onConnected() + bNotifier, bConnected := onConnected() + controllingAgent, err := NewAgent(&AgentConfig{ + NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}, + MulticastDNSMode: MulticastDNSModeDisabled, + KeepaliveInterval: &keepaliveInterval, + CheckInterval: &oneHour, + BindingRequestHandler: func(_ *stun.Message, _, _ Candidate, _ *CandidatePair) bool { + controlledLoggingFired.Store(true) + return false + }, + }) + require.NoError(t, err) + require.NoError(t, controllingAgent.OnConnectionStateChange(aNotifier)) + + controlledAgent, err := NewAgent(&AgentConfig{ + NetworkTypes: []NetworkType{NetworkTypeUDP4}, + MulticastDNSMode: MulticastDNSModeDisabled, + KeepaliveInterval: &keepaliveInterval, + CheckInterval: &oneHour, + BindingRequestHandler: func(_ *stun.Message, _, _ Candidate, _ *CandidatePair) bool { + // Don't switch candidate pair until we are ready + val, ok := switchToNewCandidatePair.Load().(bool) + return ok && val + }, + }) + require.NoError(t, err) + require.NoError(t, controlledAgent.OnConnectionStateChange(bNotifier)) + + controlledConn, controllingConn := connect(controlledAgent, controllingAgent) + <-aConnected + <-bConnected + + // Assert we have connected and can send data + require.True(t, sendUntilDone(t, controlledConn, controllingConn, 100)) + + // Take the lock on the controlling Agent and unset state + assert.NoError(t, controlledAgent.loop.Run(controlledAgent.loop, func(_ context.Context) { + for net, cs := range controlledAgent.remoteCandidates { + for _, c := range cs { + require.NoError(t, c.close()) + } + delete(controlledAgent.remoteCandidates, net) + } + + for _, c := range controlledAgent.localCandidates[NetworkTypeUDP4] { + cast, ok := c.(*CandidateHost) + require.True(t, ok) + cast.remoteCandidateCaches = map[AddrPort]Candidate{} + } + + controlledAgent.setSelectedPair(nil) + controlledAgent.checklist = make([]*CandidatePair, 0) + })) + + // Assert that Selected Candidate pair has only been unset on Controlled side + candidatePair, err := controlledAgent.GetSelectedCandidatePair() + assert.Nil(t, candidatePair) + assert.NoError(t, err) + + candidatePair, err = controllingAgent.GetSelectedCandidatePair() + assert.NotNil(t, candidatePair) + assert.NoError(t, err) + + // Sending will fail, we no longer have a selected candidate pair + require.False(t, sendUntilDone(t, controlledConn, controllingConn, 20)) + + // Send STUN Binding requests until a new Selected Candidate Pair has been set by BindingRequestHandler + switchToNewCandidatePair.Store(true) + for { + controllingAgent.requestConnectivityCheck() + + candidatePair, err = controlledAgent.GetSelectedCandidatePair() + require.NoError(t, err) + if candidatePair != nil { + break + } + + time.Sleep(time.Millisecond * 5) + } + + // We have a new selected candidate pair because of BindingRequestHandler, test that it works + require.True(t, sendUntilDone(t, controllingConn, controlledConn, 100)) + + fired, ok := controlledLoggingFired.Load().(bool) + require.True(t, ok) + require.True(t, fired) + + closePipe(t, controllingConn, controlledConn) +} From 899594c0089d4c0f992aea0c39bbd57eaa699839 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 00:10:34 +0000 Subject: [PATCH 048/114] Update module github.com/pion/dtls/v2 to v2.2.11 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6cd02ba..2fe3d52 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/google/uuid v1.6.0 - github.com/pion/dtls/v2 v2.2.10 + github.com/pion/dtls/v2 v2.2.11 github.com/pion/logging v0.2.2 github.com/pion/mdns/v2 v2.0.7 github.com/pion/randutil v0.1.0 diff --git a/go.sum b/go.sum index 01eae10..2645ff9 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= -github.com/pion/dtls/v2 v2.2.10 h1:u2Axk+FyIR1VFTPurktB+1zoEPGIW3bmyj3LEFrXjAA= -github.com/pion/dtls/v2 v2.2.10/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks= +github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= From 7e4403748034f73d220e17fc099a7d44f1e0f9c0 Mon Sep 17 00:00:00 2001 From: Juliusz Chroboczek Date: Thu, 9 May 2024 12:35:56 +0200 Subject: [PATCH 049/114] Revert "Avoid allocation storing last active time" This reverts commit edb69295c04429931ede6a3df01d49520a1b31dc. In that commit, active time was changed from time.Time to Unix time in order to avoid allocations. Unfortunately, that has the side effect of discarding the monotonic component of time.Time, and therefore makes our code vulnerable to stepping of the system clock. Fixes #697 --- candidate_base.go | 16 ++++++++-------- candidate_test.go | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/candidate_base.go b/candidate_base.go index 81b54a5..1a60899 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -31,8 +31,8 @@ type candidateBase struct { resolvedAddr net.Addr - lastSent atomic.Int64 - lastReceived atomic.Int64 + lastSent atomic.Value + lastReceived atomic.Value conn net.PacketConn currAgent *Agent @@ -409,27 +409,27 @@ func (c *candidateBase) String() string { // LastReceived returns a time.Time indicating the last time // this candidate was received func (c *candidateBase) LastReceived() time.Time { - if lastReceived := c.lastReceived.Load(); lastReceived != 0 { - return time.Unix(0, lastReceived) + if lastReceived, ok := c.lastReceived.Load().(time.Time); ok { + return lastReceived } return time.Time{} } func (c *candidateBase) setLastReceived(t time.Time) { - c.lastReceived.Store(t.UnixNano()) + c.lastReceived.Store(t) } // LastSent returns a time.Time indicating the last time // this candidate was sent func (c *candidateBase) LastSent() time.Time { - if lastSent := c.lastSent.Load(); lastSent != 0 { - return time.Unix(0, lastSent) + if lastSent, ok := c.lastSent.Load().(time.Time); ok { + return lastSent } return time.Time{} } func (c *candidateBase) setLastSent(t time.Time) { - c.lastSent.Store(t.UnixNano()) + c.lastSent.Store(t) } func (c *candidateBase) seen(outbound bool) { diff --git a/candidate_test.go b/candidate_test.go index aecbeec..04bb17c 100644 --- a/candidate_test.go +++ b/candidate_test.go @@ -186,7 +186,7 @@ func TestCandidateLastSent(t *testing.T) { require.Equal(t, candidate.LastSent(), time.Time{}) now := time.Now() candidate.setLastSent(now) - require.EqualValues(t, 0, now.Sub(candidate.LastSent())) + require.Equal(t, candidate.LastSent(), now) } func TestCandidateLastReceived(t *testing.T) { @@ -194,7 +194,7 @@ func TestCandidateLastReceived(t *testing.T) { require.Equal(t, candidate.LastReceived(), time.Time{}) now := time.Now() candidate.setLastReceived(now) - require.EqualValues(t, 0, now.Sub(candidate.LastReceived())) + require.Equal(t, candidate.LastReceived(), now) } func TestCandidateFoundation(t *testing.T) { From 2a9fdb5c0dde845df6a5cb4709e619dbb6164786 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 4 Jun 2024 20:54:20 +0000 Subject: [PATCH 050/114] Update module golang.org/x/net to v0.26.0 Generated by renovateBot --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 2fe3d52..657c132 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pion/transport/v3 v3.0.2 github.com/pion/turn/v3 v3.0.3 github.com/stretchr/testify v1.9.0 - golang.org/x/net v0.24.0 + golang.org/x/net v0.26.0 ) require ( @@ -20,8 +20,8 @@ require ( github.com/kr/pretty v0.1.0 // indirect github.com/pion/transport/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/crypto v0.22.0 // indirect - golang.org/x/sys v0.19.0 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/sys v0.21.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 2645ff9..e48314e 100644 --- a/go.sum +++ b/go.sum @@ -47,8 +47,8 @@ golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98y golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -61,8 +61,8 @@ golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -78,8 +78,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= From d8341e71aec1cd80d44ead249898be6afe16a870 Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Mon, 1 Jul 2024 12:37:20 -0400 Subject: [PATCH 051/114] Cleanly close agent goroutines --- agent.go | 12 +++++++---- agent_handlers.go | 45 +++++++++++++++++++++++++++++++++++++++++- agent_handlers_test.go | 4 ++++ agent_test.go | 5 ++++- 4 files changed, 60 insertions(+), 6 deletions(-) diff --git a/agent.go b/agent.go index 761c07c..3412e10 100644 --- a/agent.go +++ b/agent.go @@ -220,9 +220,9 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit userBindingRequestHandler: config.BindingRequestHandler, } - a.connectionStateNotifier = &handlerNotifier{connectionStateFunc: a.onConnectionStateChange} - a.candidateNotifier = &handlerNotifier{candidateFunc: a.onCandidate} - a.selectedCandidatePairNotifier = &handlerNotifier{candidatePairFunc: a.onSelectedCandidatePairChange} + a.connectionStateNotifier = &handlerNotifier{connectionStateFunc: a.onConnectionStateChange, done: make(chan struct{})} + a.candidateNotifier = &handlerNotifier{candidateFunc: a.onCandidate, done: make(chan struct{})} + a.selectedCandidatePairNotifier = &handlerNotifier{candidatePairFunc: a.onSelectedCandidatePairChange, done: make(chan struct{})} if a.net == nil { a.net, err = stdnet.NewNet() @@ -849,7 +849,11 @@ func (a *Agent) removeUfragFromMux() { // Close cleans up the Agent func (a *Agent) Close() error { - return a.loop.Close() + err := a.loop.Close() + a.connectionStateNotifier.Close() + a.candidateNotifier.Close() + a.selectedCandidatePairNotifier.Close() + return err } // Remove all candidates. This closes any listening sockets diff --git a/agent_handlers.go b/agent_handlers.go index bb0c8d3..7ebfedd 100644 --- a/agent_handlers.go +++ b/agent_handlers.go @@ -45,7 +45,8 @@ func (a *Agent) onConnectionStateChange(s ConnectionState) { type handlerNotifier struct { sync.Mutex - running bool + running bool + notifiers sync.WaitGroup connectionStates []ConnectionState connectionStateFunc func(ConnectionState) @@ -55,13 +56,38 @@ type handlerNotifier struct { selectedCandidatePairs []*CandidatePair candidatePairFunc func(*CandidatePair) + + // State for closing + done chan struct{} +} + +func (h *handlerNotifier) Close() { + h.Lock() + + select { + case <-h.done: + h.Unlock() + return + default: + } + close(h.done) + h.Unlock() + + h.notifiers.Wait() } func (h *handlerNotifier) EnqueueConnectionState(s ConnectionState) { h.Lock() defer h.Unlock() + select { + case <-h.done: + return + default: + } + notify := func() { + defer h.notifiers.Done() for { h.Lock() if len(h.connectionStates) == 0 { @@ -79,6 +105,7 @@ func (h *handlerNotifier) EnqueueConnectionState(s ConnectionState) { h.connectionStates = append(h.connectionStates, s) if !h.running { h.running = true + h.notifiers.Add(1) go notify() } } @@ -87,7 +114,14 @@ func (h *handlerNotifier) EnqueueCandidate(c Candidate) { h.Lock() defer h.Unlock() + select { + case <-h.done: + return + default: + } + notify := func() { + defer h.notifiers.Done() for { h.Lock() if len(h.candidates) == 0 { @@ -105,6 +139,7 @@ func (h *handlerNotifier) EnqueueCandidate(c Candidate) { h.candidates = append(h.candidates, c) if !h.running { h.running = true + h.notifiers.Add(1) go notify() } } @@ -113,7 +148,14 @@ func (h *handlerNotifier) EnqueueSelectedCandidatePair(p *CandidatePair) { h.Lock() defer h.Unlock() + select { + case <-h.done: + return + default: + } + notify := func() { + defer h.notifiers.Done() for { h.Lock() if len(h.selectedCandidatePairs) == 0 { @@ -131,6 +173,7 @@ func (h *handlerNotifier) EnqueueSelectedCandidatePair(p *CandidatePair) { h.selectedCandidatePairs = append(h.selectedCandidatePairs, p) if !h.running { h.running = true + h.notifiers.Add(1) go notify() } } diff --git a/agent_handlers_test.go b/agent_handlers_test.go index 35680ee..ce02733 100644 --- a/agent_handlers_test.go +++ b/agent_handlers_test.go @@ -19,6 +19,7 @@ func TestConnectionStateNotifier(t *testing.T) { connectionStateFunc: func(_ ConnectionState) { updates <- struct{}{} }, + done: make(chan struct{}), } // Enqueue all updates upfront to ensure that it // doesn't block @@ -38,6 +39,7 @@ func TestConnectionStateNotifier(t *testing.T) { close(done) }() <-done + c.Close() }) t.Run("TestUpdateOrdering", func(t *testing.T) { defer test.CheckRoutines(t)() @@ -46,6 +48,7 @@ func TestConnectionStateNotifier(t *testing.T) { connectionStateFunc: func(cs ConnectionState) { updates <- cs }, + done: make(chan struct{}), } done := make(chan struct{}) go func() { @@ -66,5 +69,6 @@ func TestConnectionStateNotifier(t *testing.T) { c.EnqueueConnectionState(ConnectionState(i)) } <-done + c.Close() }) } diff --git a/agent_test.go b/agent_test.go index 552d421..05dc19b 100644 --- a/agent_test.go +++ b/agent_test.go @@ -1379,11 +1379,12 @@ func TestCloseInConnectionStateCallback(t *testing.T) { isClosed := make(chan interface{}) isConnected := make(chan interface{}) + connectionStateConnectedSeen := make(chan interface{}) err = aAgent.OnConnectionStateChange(func(c ConnectionState) { switch c { case ConnectionStateConnected: <-isConnected - require.NoError(t, aAgent.Close()) + close(connectionStateConnectedSeen) case ConnectionStateClosed: close(isClosed) default: @@ -1393,6 +1394,8 @@ func TestCloseInConnectionStateCallback(t *testing.T) { connect(aAgent, bAgent) close(isConnected) + <-connectionStateConnectedSeen + require.NoError(t, aAgent.Close()) <-isClosed require.NoError(t, bAgent.Close()) From 11845a7f56ef1f0e6446f1880bc2119c4f1e6f4b Mon Sep 17 00:00:00 2001 From: sirzooro Date: Sat, 6 Jul 2024 14:52:09 +0200 Subject: [PATCH 052/114] Remove IPv6 ZoneID from ICE candidates (#704) Link-local IPv6 addresses may have ZoneID attached at the end. It has local meaning only and should not be send to other parties. This change removes ZoneID from generated candidate string, and ignores ZoneID when received candidate is parsed. --- candidate_base.go | 11 +++++++++-- candidate_test.go | 31 ++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/candidate_base.go b/candidate_base.go index 1a60899..e1fbfe1 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -460,6 +460,13 @@ func (c *candidateBase) copy() (Candidate, error) { return UnmarshalCandidate(c.Marshal()) } +func removeZoneIDFromAddress(addr string) string { + if i := strings.Index(addr, "%"); i != -1 { + return addr[:i] + } + return addr +} + // Marshal returns the string representation of the ICECandidate func (c *candidateBase) Marshal() string { val := c.Foundation() @@ -472,7 +479,7 @@ func (c *candidateBase) Marshal() string { c.Component(), c.NetworkType().NetworkShort(), c.Priority(), - c.Address(), + removeZoneIDFromAddress(c.Address()), c.Port(), c.Type()) @@ -522,7 +529,7 @@ func UnmarshalCandidate(raw string) (Candidate, error) { priority := uint32(priorityRaw) // Address - address := split[4] + address := removeZoneIDFromAddress(split[4]) // Port rawPort, err := strconv.ParseUint(split[5], 10, 16) diff --git a/candidate_test.go b/candidate_test.go index 04bb17c..aecea11 100644 --- a/candidate_test.go +++ b/candidate_test.go @@ -392,7 +392,7 @@ func TestCandidateMarshal(t *testing.T) { require.NoError(t, err) - require.True(t, test.candidate.Equal(actualCandidate)) + require.Truef(t, test.candidate.Equal(actualCandidate), "%s != %s", test.candidate.String(), actualCandidate.String()) require.Equal(t, test.marshaled, actualCandidate.Marshal()) }) } @@ -437,3 +437,32 @@ func TestCandidateWriteTo(t *testing.T) { _, err = c1.writeTo([]byte("test"), c2) require.Error(t, err, "writing to closed conn") } + +func TestMarshalUnmarshalCandidateWithZoneID(t *testing.T) { + candidateWithZoneID := mustCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP6.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a%Local Connection", + Port: 53987, + Priority: 500, + Foundation: "750", + }) + candidateStr := "750 0 udp 500 fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a 53987 typ host" + require.Equal(t, candidateStr, candidateWithZoneID.Marshal()) + + candidate := mustCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP6.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }) + candidateWithZoneIDStr := "750 0 udp 500 fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a%eth0 53987 typ host" + candidate2, err := UnmarshalCandidate(candidateWithZoneIDStr) + require.NoError(t, err) + require.Truef(t, candidate.Equal(candidate2), "%s != %s", candidate.String(), candidate2.String()) + + candidateWithZoneIDStr2 := "750 0 udp 500 fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a%eth0%eth1 53987 typ host" + candidate2, err = UnmarshalCandidate(candidateWithZoneIDStr2) + require.NoError(t, err) + require.Truef(t, candidate.Equal(candidate2), "%s != %s", candidate.String(), candidate2.String()) +} From 89093bb75bc280d2faf3dce7b4479f0fddc0c73c Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Mon, 15 Jul 2024 12:19:01 -0400 Subject: [PATCH 053/114] Revert "Cleanly close agent goroutines" This reverts commit d8341e71aec1cd80d44ead249898be6afe16a870. --- agent.go | 12 ++++------- agent_handlers.go | 45 +----------------------------------------- agent_handlers_test.go | 4 ---- agent_test.go | 5 +---- 4 files changed, 6 insertions(+), 60 deletions(-) diff --git a/agent.go b/agent.go index 3412e10..761c07c 100644 --- a/agent.go +++ b/agent.go @@ -220,9 +220,9 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit userBindingRequestHandler: config.BindingRequestHandler, } - a.connectionStateNotifier = &handlerNotifier{connectionStateFunc: a.onConnectionStateChange, done: make(chan struct{})} - a.candidateNotifier = &handlerNotifier{candidateFunc: a.onCandidate, done: make(chan struct{})} - a.selectedCandidatePairNotifier = &handlerNotifier{candidatePairFunc: a.onSelectedCandidatePairChange, done: make(chan struct{})} + a.connectionStateNotifier = &handlerNotifier{connectionStateFunc: a.onConnectionStateChange} + a.candidateNotifier = &handlerNotifier{candidateFunc: a.onCandidate} + a.selectedCandidatePairNotifier = &handlerNotifier{candidatePairFunc: a.onSelectedCandidatePairChange} if a.net == nil { a.net, err = stdnet.NewNet() @@ -849,11 +849,7 @@ func (a *Agent) removeUfragFromMux() { // Close cleans up the Agent func (a *Agent) Close() error { - err := a.loop.Close() - a.connectionStateNotifier.Close() - a.candidateNotifier.Close() - a.selectedCandidatePairNotifier.Close() - return err + return a.loop.Close() } // Remove all candidates. This closes any listening sockets diff --git a/agent_handlers.go b/agent_handlers.go index 7ebfedd..bb0c8d3 100644 --- a/agent_handlers.go +++ b/agent_handlers.go @@ -45,8 +45,7 @@ func (a *Agent) onConnectionStateChange(s ConnectionState) { type handlerNotifier struct { sync.Mutex - running bool - notifiers sync.WaitGroup + running bool connectionStates []ConnectionState connectionStateFunc func(ConnectionState) @@ -56,38 +55,13 @@ type handlerNotifier struct { selectedCandidatePairs []*CandidatePair candidatePairFunc func(*CandidatePair) - - // State for closing - done chan struct{} -} - -func (h *handlerNotifier) Close() { - h.Lock() - - select { - case <-h.done: - h.Unlock() - return - default: - } - close(h.done) - h.Unlock() - - h.notifiers.Wait() } func (h *handlerNotifier) EnqueueConnectionState(s ConnectionState) { h.Lock() defer h.Unlock() - select { - case <-h.done: - return - default: - } - notify := func() { - defer h.notifiers.Done() for { h.Lock() if len(h.connectionStates) == 0 { @@ -105,7 +79,6 @@ func (h *handlerNotifier) EnqueueConnectionState(s ConnectionState) { h.connectionStates = append(h.connectionStates, s) if !h.running { h.running = true - h.notifiers.Add(1) go notify() } } @@ -114,14 +87,7 @@ func (h *handlerNotifier) EnqueueCandidate(c Candidate) { h.Lock() defer h.Unlock() - select { - case <-h.done: - return - default: - } - notify := func() { - defer h.notifiers.Done() for { h.Lock() if len(h.candidates) == 0 { @@ -139,7 +105,6 @@ func (h *handlerNotifier) EnqueueCandidate(c Candidate) { h.candidates = append(h.candidates, c) if !h.running { h.running = true - h.notifiers.Add(1) go notify() } } @@ -148,14 +113,7 @@ func (h *handlerNotifier) EnqueueSelectedCandidatePair(p *CandidatePair) { h.Lock() defer h.Unlock() - select { - case <-h.done: - return - default: - } - notify := func() { - defer h.notifiers.Done() for { h.Lock() if len(h.selectedCandidatePairs) == 0 { @@ -173,7 +131,6 @@ func (h *handlerNotifier) EnqueueSelectedCandidatePair(p *CandidatePair) { h.selectedCandidatePairs = append(h.selectedCandidatePairs, p) if !h.running { h.running = true - h.notifiers.Add(1) go notify() } } diff --git a/agent_handlers_test.go b/agent_handlers_test.go index ce02733..35680ee 100644 --- a/agent_handlers_test.go +++ b/agent_handlers_test.go @@ -19,7 +19,6 @@ func TestConnectionStateNotifier(t *testing.T) { connectionStateFunc: func(_ ConnectionState) { updates <- struct{}{} }, - done: make(chan struct{}), } // Enqueue all updates upfront to ensure that it // doesn't block @@ -39,7 +38,6 @@ func TestConnectionStateNotifier(t *testing.T) { close(done) }() <-done - c.Close() }) t.Run("TestUpdateOrdering", func(t *testing.T) { defer test.CheckRoutines(t)() @@ -48,7 +46,6 @@ func TestConnectionStateNotifier(t *testing.T) { connectionStateFunc: func(cs ConnectionState) { updates <- cs }, - done: make(chan struct{}), } done := make(chan struct{}) go func() { @@ -69,6 +66,5 @@ func TestConnectionStateNotifier(t *testing.T) { c.EnqueueConnectionState(ConnectionState(i)) } <-done - c.Close() }) } diff --git a/agent_test.go b/agent_test.go index 05dc19b..552d421 100644 --- a/agent_test.go +++ b/agent_test.go @@ -1379,12 +1379,11 @@ func TestCloseInConnectionStateCallback(t *testing.T) { isClosed := make(chan interface{}) isConnected := make(chan interface{}) - connectionStateConnectedSeen := make(chan interface{}) err = aAgent.OnConnectionStateChange(func(c ConnectionState) { switch c { case ConnectionStateConnected: <-isConnected - close(connectionStateConnectedSeen) + require.NoError(t, aAgent.Close()) case ConnectionStateClosed: close(isClosed) default: @@ -1394,8 +1393,6 @@ func TestCloseInConnectionStateCallback(t *testing.T) { connect(aAgent, bAgent) close(isConnected) - <-connectionStateConnectedSeen - require.NoError(t, aAgent.Close()) <-isClosed require.NoError(t, bAgent.Close()) From 26c71deaa4b8f474585f2ce1cd8c49cad82fa15a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Jul 2024 19:31:10 +0000 Subject: [PATCH 054/114] Update module github.com/pion/dtls/v2 to v2.2.12 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 657c132..b064523 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/google/uuid v1.6.0 - github.com/pion/dtls/v2 v2.2.11 + github.com/pion/dtls/v2 v2.2.12 github.com/pion/logging v0.2.2 github.com/pion/mdns/v2 v2.0.7 github.com/pion/randutil v0.1.0 diff --git a/go.sum b/go.sum index e48314e..9245832 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= -github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks= -github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= +github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= From bbc44d00928f89d9126e2d4a16cd95a099adb55a Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Mon, 22 Jul 2024 13:03:32 -0400 Subject: [PATCH 055/114] Bump transport to v3.0.4 --- go.mod | 5 +++-- go.sum | 19 ++++++------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index b064523..384da8e 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/pion/mdns/v2 v2.0.7 github.com/pion/randutil v0.1.0 github.com/pion/stun/v2 v2.0.0 - github.com/pion/transport/v3 v3.0.2 + github.com/pion/transport/v3 v3.0.4 github.com/pion/turn/v3 v3.0.3 github.com/stretchr/testify v1.9.0 golang.org/x/net v0.26.0 @@ -20,8 +20,9 @@ require ( github.com/kr/pretty v0.1.0 // indirect github.com/pion/transport/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/wlynxg/anet v0.0.3 // indirect golang.org/x/crypto v0.24.0 // indirect - golang.org/x/sys v0.21.0 // indirect + golang.org/x/sys v0.22.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 9245832..e2a1dfa 100644 --- a/go.sum +++ b/go.sum @@ -23,8 +23,8 @@ github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1A github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo= github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= -github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4= -github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0= +github.com/pion/transport/v3 v3.0.4 h1:c9gr6afU15XfP535VjEzoHHomFSMUWTn9aLmnGZsIVk= +github.com/pion/transport/v3 v3.0.4/go.mod h1:HvJr2N/JwNJAfipsRleqwFoR3t/pWyHeZUs89v3+t5s= github.com/pion/turn/v3 v3.0.3 h1:1e3GVk8gHZLPBA5LqadWYV60lmaKUaHCkm9DX9CkGcE= github.com/pion/turn/v3 v3.0.3/go.mod h1:vw0Dz420q7VYAF3J4wJKzReLHIo2LGp4ev8nXQexYsc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -32,21 +32,20 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg= +github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -59,8 +58,6 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -76,10 +73,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -87,8 +82,6 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= From f15ba9868cbfbbbc5d529b0eb2d9c3e28800607d Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Mon, 22 Jul 2024 23:22:26 -0400 Subject: [PATCH 056/114] Bump transport to v3.0.5 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 384da8e..731e7cf 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/pion/mdns/v2 v2.0.7 github.com/pion/randutil v0.1.0 github.com/pion/stun/v2 v2.0.0 - github.com/pion/transport/v3 v3.0.4 + github.com/pion/transport/v3 v3.0.5 github.com/pion/turn/v3 v3.0.3 github.com/stretchr/testify v1.9.0 golang.org/x/net v0.26.0 diff --git a/go.sum b/go.sum index e2a1dfa..22a1dc7 100644 --- a/go.sum +++ b/go.sum @@ -23,8 +23,8 @@ github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1A github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo= github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= -github.com/pion/transport/v3 v3.0.4 h1:c9gr6afU15XfP535VjEzoHHomFSMUWTn9aLmnGZsIVk= -github.com/pion/transport/v3 v3.0.4/go.mod h1:HvJr2N/JwNJAfipsRleqwFoR3t/pWyHeZUs89v3+t5s= +github.com/pion/transport/v3 v3.0.5 h1:ofVrcbPNqVPuKaTO5AMFnFuJ1ZX7ElYiWzC5PCf9YVQ= +github.com/pion/transport/v3 v3.0.5/go.mod h1:HvJr2N/JwNJAfipsRleqwFoR3t/pWyHeZUs89v3+t5s= github.com/pion/turn/v3 v3.0.3 h1:1e3GVk8gHZLPBA5LqadWYV60lmaKUaHCkm9DX9CkGcE= github.com/pion/turn/v3 v3.0.3/go.mod h1:vw0Dz420q7VYAF3J4wJKzReLHIo2LGp4ev8nXQexYsc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= From abf50f9c340b0517448a15953cd3346dca708c80 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Thu, 25 Jul 2024 11:18:50 -0400 Subject: [PATCH 057/114] Don't allocate new error inside TaskLoop Libraries use errors.Is to catch this error. Allocating a new one inside internal breaks that --- errors.go | 8 ++++++-- internal/taskloop/taskloop.go | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/errors.go b/errors.go index e39c7cf..e9bfe07 100644 --- a/errors.go +++ b/errors.go @@ -3,7 +3,11 @@ package ice -import "errors" +import ( + "errors" + + "github.com/pion/ice/v3/internal/taskloop" +) var ( // ErrUnknownType indicates an error with Unknown info. @@ -36,7 +40,7 @@ var ( ErrProtoType = errors.New("invalid transport protocol type") // ErrClosed indicates the agent is closed - ErrClosed = errors.New("the agent is closed") + ErrClosed = taskloop.ErrClosed // ErrNoCandidatePairs indicates agent does not have a valid candidate pair ErrNoCandidatePairs = errors.New("no candidate pairs available") diff --git a/internal/taskloop/taskloop.go b/internal/taskloop/taskloop.go index 2e55dc3..b850ab4 100644 --- a/internal/taskloop/taskloop.go +++ b/internal/taskloop/taskloop.go @@ -13,8 +13,8 @@ import ( atomicx "github.com/pion/ice/v3/internal/atomic" ) -// errClosed indicates that the loop has been stopped -var errClosed = errors.New("the agent is closed") +// ErrClosed indicates that the loop has been stopped +var ErrClosed = errors.New("the agent is closed") type task struct { fn func(context.Context) @@ -68,7 +68,7 @@ func (l *Loop) Close() error { return err } - l.err.Store(errClosed) + l.err.Store(ErrClosed) close(l.done) <-l.taskLoopDone @@ -104,7 +104,7 @@ func (l *Loop) Done() <-chan struct{} { func (l *Loop) Err() error { select { case <-l.done: - return errClosed + return ErrClosed default: return nil } From a0385eec1a842feabe92ad2b922b28226c319ed1 Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Tue, 23 Jul 2024 14:51:13 -0400 Subject: [PATCH 058/114] Add GracefulClose --- agent.go | 27 ++++++++++++++++++---- agent_handlers.go | 47 +++++++++++++++++++++++++++++++++++++- agent_handlers_test.go | 4 ++++ agent_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 5 deletions(-) diff --git a/agent.go b/agent.go index 761c07c..f8ee66e 100644 --- a/agent.go +++ b/agent.go @@ -220,9 +220,9 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit userBindingRequestHandler: config.BindingRequestHandler, } - a.connectionStateNotifier = &handlerNotifier{connectionStateFunc: a.onConnectionStateChange} - a.candidateNotifier = &handlerNotifier{candidateFunc: a.onCandidate} - a.selectedCandidatePairNotifier = &handlerNotifier{candidatePairFunc: a.onSelectedCandidatePairChange} + a.connectionStateNotifier = &handlerNotifier{connectionStateFunc: a.onConnectionStateChange, done: make(chan struct{})} + a.candidateNotifier = &handlerNotifier{candidateFunc: a.onCandidate, done: make(chan struct{})} + a.selectedCandidatePairNotifier = &handlerNotifier{candidatePairFunc: a.onSelectedCandidatePairChange, done: make(chan struct{})} if a.net == nil { a.net, err = stdnet.NewNet() @@ -849,7 +849,26 @@ func (a *Agent) removeUfragFromMux() { // Close cleans up the Agent func (a *Agent) Close() error { - return a.loop.Close() + return a.close(false) +} + +// GracefulClose cleans up the Agent and waits for any goroutines it started +// to complete. This is only safe to call outside of Agent callbacks or if in a callback, +// in its own goroutine. +func (a *Agent) GracefulClose() error { + return a.close(true) +} + +func (a *Agent) close(graceful bool) error { + // the loop is safe to wait on no matter what + err := a.loop.Close() + + // but we are in less control of the notifiers, so we will + // pass through `graceful`. + a.connectionStateNotifier.Close(graceful) + a.candidateNotifier.Close(graceful) + a.selectedCandidatePairNotifier.Close(graceful) + return err } // Remove all candidates. This closes any listening sockets diff --git a/agent_handlers.go b/agent_handlers.go index bb0c8d3..3de9f32 100644 --- a/agent_handlers.go +++ b/agent_handlers.go @@ -45,7 +45,8 @@ func (a *Agent) onConnectionStateChange(s ConnectionState) { type handlerNotifier struct { sync.Mutex - running bool + running bool + notifiers sync.WaitGroup connectionStates []ConnectionState connectionStateFunc func(ConnectionState) @@ -55,13 +56,40 @@ type handlerNotifier struct { selectedCandidatePairs []*CandidatePair candidatePairFunc func(*CandidatePair) + + // State for closing + done chan struct{} +} + +func (h *handlerNotifier) Close(graceful bool) { + h.Lock() + + select { + case <-h.done: + h.Unlock() + return + default: + } + close(h.done) + h.Unlock() + + if graceful { + h.notifiers.Wait() + } } func (h *handlerNotifier) EnqueueConnectionState(s ConnectionState) { h.Lock() defer h.Unlock() + select { + case <-h.done: + return + default: + } + notify := func() { + defer h.notifiers.Done() for { h.Lock() if len(h.connectionStates) == 0 { @@ -79,6 +107,7 @@ func (h *handlerNotifier) EnqueueConnectionState(s ConnectionState) { h.connectionStates = append(h.connectionStates, s) if !h.running { h.running = true + h.notifiers.Add(1) go notify() } } @@ -87,7 +116,14 @@ func (h *handlerNotifier) EnqueueCandidate(c Candidate) { h.Lock() defer h.Unlock() + select { + case <-h.done: + return + default: + } + notify := func() { + defer h.notifiers.Done() for { h.Lock() if len(h.candidates) == 0 { @@ -105,6 +141,7 @@ func (h *handlerNotifier) EnqueueCandidate(c Candidate) { h.candidates = append(h.candidates, c) if !h.running { h.running = true + h.notifiers.Add(1) go notify() } } @@ -113,7 +150,14 @@ func (h *handlerNotifier) EnqueueSelectedCandidatePair(p *CandidatePair) { h.Lock() defer h.Unlock() + select { + case <-h.done: + return + default: + } + notify := func() { + defer h.notifiers.Done() for { h.Lock() if len(h.selectedCandidatePairs) == 0 { @@ -131,6 +175,7 @@ func (h *handlerNotifier) EnqueueSelectedCandidatePair(p *CandidatePair) { h.selectedCandidatePairs = append(h.selectedCandidatePairs, p) if !h.running { h.running = true + h.notifiers.Add(1) go notify() } } diff --git a/agent_handlers_test.go b/agent_handlers_test.go index 35680ee..c708c09 100644 --- a/agent_handlers_test.go +++ b/agent_handlers_test.go @@ -19,6 +19,7 @@ func TestConnectionStateNotifier(t *testing.T) { connectionStateFunc: func(_ ConnectionState) { updates <- struct{}{} }, + done: make(chan struct{}), } // Enqueue all updates upfront to ensure that it // doesn't block @@ -38,6 +39,7 @@ func TestConnectionStateNotifier(t *testing.T) { close(done) }() <-done + c.Close(true) }) t.Run("TestUpdateOrdering", func(t *testing.T) { defer test.CheckRoutines(t)() @@ -46,6 +48,7 @@ func TestConnectionStateNotifier(t *testing.T) { connectionStateFunc: func(cs ConnectionState) { updates <- cs }, + done: make(chan struct{}), } done := make(chan struct{}) go func() { @@ -66,5 +69,6 @@ func TestConnectionStateNotifier(t *testing.T) { c.EnqueueConnectionState(ConnectionState(i)) } <-done + c.Close(true) }) } diff --git a/agent_test.go b/agent_test.go index 552d421..6f54035 100644 --- a/agent_test.go +++ b/agent_test.go @@ -1737,3 +1737,55 @@ func TestAcceptAggressiveNomination(t *testing.T) { require.NoError(t, wan.Stop()) closePipe(t, aConn, bConn) } + +// Close can deadlock but GracefulClose must not +func TestAgentGracefulCloseDeadlock(t *testing.T) { + defer test.CheckRoutinesStrict(t)() + defer test.TimeOut(time.Second * 5).Stop() + + config := &AgentConfig{ + NetworkTypes: supportedNetworkTypes(), + } + aAgent, err := NewAgent(config) + require.NoError(t, err) + + bAgent, err := NewAgent(config) + require.NoError(t, err) + + var connected, closeNow, closed sync.WaitGroup + connected.Add(2) + closeNow.Add(1) + closed.Add(2) + closeHdlr := func(agent *Agent) { + check(agent.OnConnectionStateChange(func(cs ConnectionState) { + if cs == ConnectionStateConnected { + connected.Done() + closeNow.Wait() + + go func() { + if err := agent.GracefulClose(); err != nil { + require.NoError(t, err) + } + closed.Done() + }() + } + })) + } + + closeHdlr(aAgent) + closeHdlr(bAgent) + + t.Log("connecting agents") + _, _ = connect(aAgent, bAgent) + + t.Log("waiting for them to confirm connection in callback") + connected.Wait() + + t.Log("tell them to close themselves in the same callback and wait") + closeNow.Done() + closed.Wait() + + // already closed + require.Error(t, aAgent.Close()) + require.Error(t, bAgent.Close()) +} From def1670796f6af2dd505cbaf858f066e2cb8573e Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Thu, 25 Jul 2024 16:27:33 -0400 Subject: [PATCH 059/114] Clean up Agents in tests more aggressively --- active_tcp_test.go | 22 +- ..._get_best_available_candidate_pair_test.go | 17 +- agent_get_best_valid_candidate_pair_test.go | 5 +- ..._on_selected_candidate_pair_change_test.go | 4 +- agent_test.go | 249 +++++++++++++----- agent_udpmux_test.go | 16 ++ candidate_relay_test.go | 13 +- candidate_server_reflexive_test.go | 13 +- connectivity_vnet_test.go | 16 +- gather_test.go | 84 ++++-- gather_vnet_test.go | 48 ++-- go.mod | 2 +- go.sum | 4 +- mdns_test.go | 22 +- transport_test.go | 13 +- transport_vnet_test.go | 5 +- udp_mux_test.go | 4 +- 17 files changed, 361 insertions(+), 176 deletions(-) diff --git a/active_tcp_test.go b/active_tcp_test.go index 44d6a47..fc51844 100644 --- a/active_tcp_test.go +++ b/active_tcp_test.go @@ -71,7 +71,7 @@ func TestActiveTCP(t *testing.T) { networkTypes: []NetworkType{NetworkTypeTCP6}, listenIPAddress: getLocalIPAddress(t, NetworkTypeTCP6), selectedPairNetworkType: tcp, - // if we don't use mDNS, we will very liekly be filtering out location tracked ips. + // if we don't use mDNS, we will very likely be filtering out location tracked ips. useMDNS: true, }, testCase{ @@ -79,7 +79,7 @@ func TestActiveTCP(t *testing.T) { networkTypes: supportedNetworkTypes(), listenIPAddress: getLocalIPAddress(t, NetworkTypeTCP6), selectedPairNetworkType: udp, - // if we don't use mDNS, we will very liekly be filtering out location tracked ips. + // if we don't use mDNS, we will very likely be filtering out location tracked ips. useMDNS: true, }, ) @@ -143,6 +143,11 @@ func TestActiveTCP(t *testing.T) { r.NotNil(passiveAgentConn) r.NotNil(activeAgenConn) + defer func() { + r.NoError(activeAgenConn.Close()) + r.NoError(passiveAgentConn.Close()) + }() + pair := passiveAgent.getSelectedPair() r.NotNil(pair) r.Equal(testCase.selectedPairNetworkType, pair.Local.NetworkType().NetworkShort()) @@ -163,9 +168,6 @@ func TestActiveTCP(t *testing.T) { n, err = passiveAgentConn.Read(buffer) r.NoError(err) r.Equal(bar, buffer[:n]) - - r.NoError(activeAgenConn.Close()) - r.NoError(passiveAgentConn.Close()) }) } } @@ -185,9 +187,17 @@ func TestActiveTCP_NonBlocking(t *testing.T) { aAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + require.NoError(t, aAgent.Close()) + }() + bAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + require.NoError(t, bAgent.Close()) + }() + isConnected := make(chan interface{}) err = aAgent.OnConnectionStateChange(func(c ConnectionState) { if c == ConnectionStateConnected { @@ -205,6 +215,4 @@ func TestActiveTCP_NonBlocking(t *testing.T) { connect(aAgent, bAgent) <-isConnected - require.NoError(t, aAgent.Close()) - require.NoError(t, bAgent.Close()) } diff --git a/agent_get_best_available_candidate_pair_test.go b/agent_get_best_available_candidate_pair_test.go index 44c6a78..c7bb2e4 100644 --- a/agent_get_best_available_candidate_pair_test.go +++ b/agent_get_best_available_candidate_pair_test.go @@ -13,19 +13,12 @@ import ( ) func TestNoBestAvailableCandidatePairAfterAgentConstruction(t *testing.T) { - agent := setupTest(t) - - require.Nil(t, agent.getBestAvailableCandidatePair()) - - tearDownTest(t, agent) -} - -func setupTest(t *testing.T) *Agent { agent, err := NewAgent(&AgentConfig{}) require.NoError(t, err) - return agent -} -func tearDownTest(t *testing.T, agent *Agent) { - require.NoError(t, agent.Close()) + defer func() { + require.NoError(t, agent.Close()) + }() + + require.Nil(t, agent.getBestAvailableCandidatePair()) } diff --git a/agent_get_best_valid_candidate_pair_test.go b/agent_get_best_valid_candidate_pair_test.go index 24e3c47..2ab2069 100644 --- a/agent_get_best_valid_candidate_pair_test.go +++ b/agent_get_best_valid_candidate_pair_test.go @@ -14,6 +14,9 @@ import ( func TestAgentGetBestValidCandidatePair(t *testing.T) { f := setupTestAgentGetBestValidCandidatePair(t) + defer func() { + require.NoError(t, f.sut.Close()) + }() remoteCandidatesFromLowestPriorityToHighest := []Candidate{f.relayRemote, f.srflxRemote, f.prflxRemote, f.hostRemote} @@ -26,8 +29,6 @@ func TestAgentGetBestValidCandidatePair(t *testing.T) { require.Equal(t, actualBestPair.String(), expectedBestPair.String()) } - - require.NoError(t, f.sut.Close()) } func setupTestAgentGetBestValidCandidatePair(t *testing.T) *TestAgentGetBestValidCandidatePairFixture { diff --git a/agent_on_selected_candidate_pair_change_test.go b/agent_on_selected_candidate_pair_change_test.go index b744749..6ac2149 100644 --- a/agent_on_selected_candidate_pair_change_test.go +++ b/agent_on_selected_candidate_pair_change_test.go @@ -15,6 +15,9 @@ import ( func TestOnSelectedCandidatePairChange(t *testing.T) { agent, candidatePair := fixtureTestOnSelectedCandidatePairChange(t) + defer func() { + require.NoError(t, agent.Close()) + }() callbackCalled := make(chan struct{}, 1) err := agent.OnSelectedCandidatePairChange(func(_, _ Candidate) { @@ -28,7 +31,6 @@ func TestOnSelectedCandidatePairChange(t *testing.T) { require.NoError(t, err) <-callbackCalled - require.NoError(t, agent.Close()) } func fixtureTestOnSelectedCandidatePairChange(t *testing.T) (*Agent, *CandidatePair) { diff --git a/agent_test.go b/agent_test.go index 6f54035..401a7f1 100644 --- a/agent_test.go +++ b/agent_test.go @@ -42,6 +42,9 @@ func TestHandlePeerReflexive(t *testing.T) { t.Run("UDP prflx candidate from handleInbound()", func(t *testing.T) { a, err := NewAgent(&AgentConfig{}) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() require.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { a.selector = &controllingSelector{agent: a, log: a.log} @@ -98,12 +101,14 @@ func TestHandlePeerReflexive(t *testing.T) { t.Fatal("Port number mismatch") } })) - require.NoError(t, a.Close()) }) t.Run("Bad network type with handleInbound()", func(t *testing.T) { a, err := NewAgent(&AgentConfig{}) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() require.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { a.selector = &controllingSelector{agent: a, log: a.log} @@ -128,13 +133,14 @@ func TestHandlePeerReflexive(t *testing.T) { t.Fatal("bad address should not be added to the remote candidate list") } })) - - require.NoError(t, a.Close()) }) t.Run("Success from unknown remote, prflx candidate MUST only be created via Binding Request", func(t *testing.T) { a, err := NewAgent(&AgentConfig{}) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() require.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { a.selector = &controllingSelector{agent: a, log: a.log} @@ -169,8 +175,6 @@ func TestHandlePeerReflexive(t *testing.T) { t.Fatal("unknown remote was able to create a candidate") } })) - - require.NoError(t, a.Close()) }) } @@ -216,6 +220,9 @@ func TestConnectivityOnStartup(t *testing.T) { aAgent, err := NewAgent(cfg0) require.NoError(t, err) + defer func() { + require.NoError(t, aAgent.Close()) + }() require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) cfg1 := &AgentConfig{ @@ -228,9 +235,12 @@ func TestConnectivityOnStartup(t *testing.T) { bAgent, err := NewAgent(cfg1) require.NoError(t, err) + defer func() { + require.NoError(t, bAgent.Close()) + }() require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) - aConn, bConn := func(aAgent, bAgent *Agent) (*Conn, *Conn) { + func(aAgent, bAgent *Agent) (*Conn, *Conn) { // Manual signaling aUfrag, aPwd, err := aAgent.GetLocalUserCredentials() require.NoError(t, err) @@ -280,7 +290,6 @@ func TestConnectivityOnStartup(t *testing.T) { <-bConnected require.NoError(t, wan.Stop()) - closePipe(t, aConn, bConn) } func TestConnectivityLite(t *testing.T) { @@ -315,6 +324,9 @@ func TestConnectivityLite(t *testing.T) { aAgent, err := NewAgent(cfg0) require.NoError(t, err) + defer func() { + require.NoError(t, aAgent.Close()) + }() require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) cfg1 := &AgentConfig{ @@ -328,16 +340,17 @@ func TestConnectivityLite(t *testing.T) { bAgent, err := NewAgent(cfg1) require.NoError(t, err) + defer func() { + require.NoError(t, bAgent.Close()) + }() require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) - aConn, bConn := connectWithVNet(aAgent, bAgent) + connectWithVNet(aAgent, bAgent) // Ensure pair selected // Note: this assumes ConnectionStateConnected is thrown after selecting the final pair <-aConnected <-bConnected - - closePipe(t, aConn, bConn) } func TestInboundValidity(t *testing.T) { @@ -372,6 +385,9 @@ func TestInboundValidity(t *testing.T) { if err != nil { t.Fatalf("Error constructing ice.Agent") } + defer func() { + require.NoError(t, a.Close()) + }() a.handleInbound(buildMsg(stun.ClassRequest, "invalid", a.localPwd), local, remote) if len(a.remoteCandidates) == 1 { @@ -382,8 +398,6 @@ func TestInboundValidity(t *testing.T) { if len(a.remoteCandidates) == 1 { t.Fatal("Binding with invalid MessageIntegrity was able to create prflx candidate") } - - require.NoError(t, a.Close()) }) t.Run("Invalid Binding success responses should be discarded", func(t *testing.T) { @@ -391,13 +405,14 @@ func TestInboundValidity(t *testing.T) { if err != nil { t.Fatalf("Error constructing ice.Agent") } + defer func() { + require.NoError(t, a.Close()) + }() a.handleInbound(buildMsg(stun.ClassSuccessResponse, a.localUfrag+":"+a.remoteUfrag, "Invalid"), local, remote) if len(a.remoteCandidates) == 1 { t.Fatal("Binding with invalid MessageIntegrity was able to create prflx candidate") } - - require.NoError(t, a.Close()) }) t.Run("Discard non-binding messages", func(t *testing.T) { @@ -405,13 +420,14 @@ func TestInboundValidity(t *testing.T) { if err != nil { t.Fatalf("Error constructing ice.Agent") } + defer func() { + require.NoError(t, a.Close()) + }() a.handleInbound(buildMsg(stun.ClassErrorResponse, a.localUfrag+":"+a.remoteUfrag, "Invalid"), local, remote) if len(a.remoteCandidates) == 1 { t.Fatal("non-binding message was able to create prflxRemote") } - - require.NoError(t, a.Close()) }) t.Run("Valid bind request", func(t *testing.T) { @@ -419,6 +435,9 @@ func TestInboundValidity(t *testing.T) { if err != nil { t.Fatalf("Error constructing ice.Agent") } + defer func() { + require.NoError(t, a.Close()) + }() err = a.loop.Run(a.loop, func(_ context.Context) { a.selector = &controllingSelector{agent: a, log: a.log} @@ -430,12 +449,14 @@ func TestInboundValidity(t *testing.T) { }) require.NoError(t, err) - require.NoError(t, a.Close()) }) t.Run("Valid bind without fingerprint", func(t *testing.T) { a, err := NewAgent(&AgentConfig{}) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() require.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { a.selector = &controllingSelector{agent: a, log: a.log} @@ -451,8 +472,6 @@ func TestInboundValidity(t *testing.T) { t.Fatal("Binding with valid values (but no fingerprint) was unable to create prflx candidate") } })) - - require.NoError(t, a.Close()) }) t.Run("Success with invalid TransactionID", func(t *testing.T) { @@ -460,6 +479,9 @@ func TestInboundValidity(t *testing.T) { if err != nil { t.Fatalf("Error constructing ice.Agent") } + defer func() { + require.NoError(t, a.Close()) + }() hostConfig := CandidateHostConfig{ Network: "udp", @@ -486,8 +508,6 @@ func TestInboundValidity(t *testing.T) { if len(a.remoteCandidates) != 0 { t.Fatal("unknown remote was able to create a candidate") } - - require.NoError(t, a.Close()) }) } @@ -496,6 +516,9 @@ func TestInvalidAgentStarts(t *testing.T) { a, err := NewAgent(&AgentConfig{}) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() ctx := context.Background() ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) @@ -516,8 +539,6 @@ func TestInvalidAgentStarts(t *testing.T) { if _, err = a.Dial(context.TODO(), "foo", "bar"); err != nil && !errors.Is(err, ErrMultipleStart) { t.Fatal(err) } - - require.NoError(t, a.Close()) } // Assert that Agent emits Connecting/Connected/Disconnected/Failed/Closed messages @@ -539,17 +560,34 @@ func TestConnectionStateCallback(t *testing.T) { InterfaceFilter: problematicNetworkInterfaces, } + isClosed := make(chan interface{}) + aAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + select { + case <-isClosed: + return + default: + } + require.NoError(t, aAgent.Close()) + }() bAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + select { + case <-isClosed: + return + default: + } + require.NoError(t, bAgent.Close()) + }() isChecking := make(chan interface{}) isConnected := make(chan interface{}) isDisconnected := make(chan interface{}) isFailed := make(chan interface{}) - isClosed := make(chan interface{}) err = aAgent.OnConnectionStateChange(func(c ConnectionState) { switch c { case ConnectionStateChecking: @@ -586,12 +624,14 @@ func TestInvalidGather(t *testing.T) { if err != nil { t.Fatalf("Error constructing ice.Agent") } + defer func() { + require.NoError(t, a.Close()) + }() err = a.GatherCandidates() if !errors.Is(err, ErrNoOnCandidateHandler) { t.Fatal("trickle GatherCandidates succeeded without OnCandidate") } - require.NoError(t, a.Close()) }) } @@ -605,6 +645,9 @@ func TestCandidatePairStats(t *testing.T) { if err != nil { t.Fatalf("Failed to create agent: %s", err) } + defer func() { + require.NoError(t, a.Close()) + }() hostConfig := &CandidateHostConfig{ Network: "udp", @@ -723,8 +766,6 @@ func TestCandidatePairStats(t *testing.T) { t.Fatalf("expected host-prflx pair to have state failed, it has state %s instead", prflxPairStat.State.String()) } - - require.NoError(t, a.Close()) } func TestLocalCandidateStats(t *testing.T) { @@ -737,6 +778,9 @@ func TestLocalCandidateStats(t *testing.T) { if err != nil { t.Fatalf("Failed to create agent: %s", err) } + defer func() { + require.NoError(t, a.Close()) + }() hostConfig := &CandidateHostConfig{ Network: "udp", @@ -803,8 +847,6 @@ func TestLocalCandidateStats(t *testing.T) { if srflxLocalStat.ID != srflxLocal.ID() { t.Fatal("missing srflx local stat") } - - require.NoError(t, a.Close()) } func TestRemoteCandidateStats(t *testing.T) { @@ -817,6 +859,9 @@ func TestRemoteCandidateStats(t *testing.T) { if err != nil { t.Fatalf("Failed to create agent: %s", err) } + defer func() { + require.NoError(t, a.Close()) + }() relayConfig := &CandidateRelayConfig{ Network: "udp", @@ -922,8 +967,6 @@ func TestRemoteCandidateStats(t *testing.T) { if hostRemoteStat.ID != hostRemote.ID() { t.Fatal("missing host remote stat") } - - require.NoError(t, a.Close()) } func TestInitExtIPMapping(t *testing.T) { @@ -935,6 +978,7 @@ func TestInitExtIPMapping(t *testing.T) { t.Fatalf("Failed to create agent: %v", err) } if a.extIPMapper != nil { + require.NoError(t, a.Close()) t.Fatal("a.extIPMapper should be nil by default") } require.NoError(t, a.Close()) @@ -945,9 +989,11 @@ func TestInitExtIPMapping(t *testing.T) { NAT1To1IPCandidateType: CandidateTypeHost, }) if err != nil { + require.NoError(t, a.Close()) t.Fatalf("Failed to create agent: %v", err) } if a.extIPMapper != nil { + require.NoError(t, a.Close()) t.Fatal("a.extIPMapper should be nil by default") } require.NoError(t, a.Close()) @@ -1002,6 +1048,9 @@ func TestBindingRequestTimeout(t *testing.T) { a, err := NewAgent(&AgentConfig{}) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() now := time.Now() a.pendingBindingRequests = append(a.pendingBindingRequests, bindingRequest{ @@ -1019,7 +1068,6 @@ func TestBindingRequestTimeout(t *testing.T) { a.invalidatePendingBindingRequests(now) require.Equal(t, expectedRemovalCount, len(a.pendingBindingRequests), "Binding invalidation due to timeout did not remove the correct number of binding requests") - require.NoError(t, a.Close()) } // TestAgentCredentials checks if local username fragments and passwords (if set) meet RFC standard @@ -1036,9 +1084,11 @@ func TestAgentCredentials(t *testing.T) { agent, err := NewAgent(&AgentConfig{LoggerFactory: log}) require.NoError(t, err) + defer func() { + require.NoError(t, agent.Close()) + }() require.GreaterOrEqual(t, len([]rune(agent.localUfrag))*8, 24) require.GreaterOrEqual(t, len([]rune(agent.localPwd))*8, 128) - require.NoError(t, agent.Close()) // Should honor RFC standards // Local values MUST be unguessable, with at least 128 bits of @@ -1071,9 +1121,15 @@ func TestConnectionStateFailedDeleteAllCandidates(t *testing.T) { aAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + require.NoError(t, aAgent.Close()) + }() bAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + require.NoError(t, bAgent.Close()) + }() isFailed := make(chan interface{}) require.NoError(t, aAgent.OnConnectionStateChange(func(c ConnectionState) { @@ -1092,9 +1148,6 @@ func TestConnectionStateFailedDeleteAllCandidates(t *testing.T) { close(done) })) <-done - - require.NoError(t, aAgent.Close()) - require.NoError(t, bAgent.Close()) } // Assert that the ICE Agent can go directly from Connecting -> Failed on both sides @@ -1114,9 +1167,15 @@ func TestConnectionStateConnectingToFailed(t *testing.T) { aAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + require.NoError(t, aAgent.Close()) + }() bAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + require.NoError(t, bAgent.Close()) + }() var isFailed sync.WaitGroup var isChecking sync.WaitGroup @@ -1151,9 +1210,6 @@ func TestConnectionStateConnectingToFailed(t *testing.T) { isChecking.Wait() isFailed.Wait() - - require.NoError(t, aAgent.Close()) - require.NoError(t, bAgent.Close()) } func TestAgentRestart(t *testing.T) { @@ -1168,6 +1224,7 @@ func TestAgentRestart(t *testing.T) { DisconnectedTimeout: &oneSecond, FailedTimeout: &oneSecond, }) + defer closePipe(t, connA, connB) ctx, cancel := context.WithCancel(context.Background()) require.NoError(t, connB.agent.OnConnectionStateChange(func(c ConnectionState) { @@ -1180,8 +1237,6 @@ func TestAgentRestart(t *testing.T) { require.NoError(t, connA.agent.Restart("", "")) <-ctx.Done() - require.NoError(t, connA.agent.Close()) - require.NoError(t, connB.agent.Close()) }) t.Run("Restart When Closed", func(t *testing.T) { @@ -1197,6 +1252,7 @@ func TestAgentRestart(t *testing.T) { DisconnectedTimeout: &oneSecond, FailedTimeout: &oneSecond, }) + defer closePipe(t, connA, connB) ctx, cancel := context.WithCancel(context.Background()) require.NoError(t, connB.agent.OnConnectionStateChange(func(c ConnectionState) { @@ -1207,8 +1263,6 @@ func TestAgentRestart(t *testing.T) { require.NoError(t, connA.agent.Restart("", "")) <-ctx.Done() - require.NoError(t, connA.agent.Close()) - require.NoError(t, connB.agent.Close()) }) t.Run("Restart Both Sides", func(t *testing.T) { @@ -1228,6 +1282,7 @@ func TestAgentRestart(t *testing.T) { DisconnectedTimeout: &oneSecond, FailedTimeout: &oneSecond, }) + defer closePipe(t, connA, connB) connAFirstCandidates := generateCandidateAddressStrings(connA.agent.GetLocalCandidates()) connBFirstCandidates := generateCandidateAddressStrings(connB.agent.GetLocalCandidates()) @@ -1259,9 +1314,6 @@ func TestAgentRestart(t *testing.T) { // Assert that we have new candidates each time require.NotEqual(t, connAFirstCandidates, generateCandidateAddressStrings(connA.agent.GetLocalCandidates())) require.NotEqual(t, connBFirstCandidates, generateCandidateAddressStrings(connB.agent.GetLocalCandidates())) - - require.NoError(t, connA.agent.Close()) - require.NoError(t, connB.agent.Close()) }) } @@ -1271,6 +1323,9 @@ func TestGetRemoteCredentials(t *testing.T) { if err != nil { t.Fatalf("Error constructing ice.Agent: %v", err) } + defer func() { + require.NoError(t, a.Close()) + }() a.remoteUfrag = "remoteUfrag" a.remotePwd = "remotePwd" @@ -1280,8 +1335,6 @@ func TestGetRemoteCredentials(t *testing.T) { require.Equal(t, actualUfrag, a.remoteUfrag) require.Equal(t, actualPwd, a.remotePwd) - - require.NoError(t, a.Close()) } func TestGetRemoteCandidates(t *testing.T) { @@ -1291,6 +1344,9 @@ func TestGetRemoteCandidates(t *testing.T) { if err != nil { t.Fatalf("Error constructing ice.Agent: %v", err) } + defer func() { + require.NoError(t, a.Close()) + }() expectedCandidates := []Candidate{} @@ -1313,8 +1369,6 @@ func TestGetRemoteCandidates(t *testing.T) { actualCandidates, err := a.GetRemoteCandidates() require.NoError(t, err) require.ElementsMatch(t, expectedCandidates, actualCandidates) - - require.NoError(t, a.Close()) } func TestGetLocalCandidates(t *testing.T) { @@ -1324,6 +1378,9 @@ func TestGetLocalCandidates(t *testing.T) { if err != nil { t.Fatalf("Error constructing ice.Agent: %v", err) } + defer func() { + require.NoError(t, a.Close()) + }() dummyConn := &net.UDPConn{} expectedCandidates := []Candidate{} @@ -1348,8 +1405,6 @@ func TestGetLocalCandidates(t *testing.T) { actualCandidates, err := a.GetLocalCandidates() require.NoError(t, err) require.ElementsMatch(t, expectedCandidates, actualCandidates) - - require.NoError(t, a.Close()) } func TestCloseInConnectionStateCallback(t *testing.T) { @@ -1373,9 +1428,19 @@ func TestCloseInConnectionStateCallback(t *testing.T) { aAgent, err := NewAgent(cfg) require.NoError(t, err) + var aAgentClosed bool + defer func() { + if aAgentClosed { + return + } + require.NoError(t, aAgent.Close()) + }() bAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + require.NoError(t, bAgent.Close()) + }() isClosed := make(chan interface{}) isConnected := make(chan interface{}) @@ -1384,6 +1449,7 @@ func TestCloseInConnectionStateCallback(t *testing.T) { case ConnectionStateConnected: <-isConnected require.NoError(t, aAgent.Close()) + aAgentClosed = true case ConnectionStateClosed: close(isClosed) default: @@ -1395,7 +1461,6 @@ func TestCloseInConnectionStateCallback(t *testing.T) { close(isConnected) <-isClosed - require.NoError(t, bAgent.Close()) } func TestRunTaskInConnectionStateCallback(t *testing.T) { @@ -1418,8 +1483,14 @@ func TestRunTaskInConnectionStateCallback(t *testing.T) { aAgent, err := NewAgent(cfg) check(err) + defer func() { + require.NoError(t, aAgent.Close()) + }() bAgent, err := NewAgent(cfg) check(err) + defer func() { + require.NoError(t, bAgent.Close()) + }() isComplete := make(chan interface{}) err = aAgent.OnConnectionStateChange(func(c ConnectionState) { @@ -1435,8 +1506,6 @@ func TestRunTaskInConnectionStateCallback(t *testing.T) { connect(aAgent, bAgent) <-isComplete - require.NoError(t, aAgent.Close()) - require.NoError(t, bAgent.Close()) } func TestRunTaskInSelectedCandidatePairChangeCallback(t *testing.T) { @@ -1459,8 +1528,14 @@ func TestRunTaskInSelectedCandidatePairChangeCallback(t *testing.T) { aAgent, err := NewAgent(cfg) check(err) + defer func() { + require.NoError(t, aAgent.Close()) + }() bAgent, err := NewAgent(cfg) check(err) + defer func() { + require.NoError(t, bAgent.Close()) + }() isComplete := make(chan interface{}) isTested := make(chan interface{}) @@ -1485,8 +1560,6 @@ func TestRunTaskInSelectedCandidatePairChangeCallback(t *testing.T) { <-isComplete <-isTested - require.NoError(t, aAgent.Close()) - require.NoError(t, bAgent.Close()) } // Assert that a Lite agent goes to disconnected and failed @@ -1502,6 +1575,13 @@ func TestLiteLifecycle(t *testing.T) { MulticastDNSMode: MulticastDNSModeDisabled, }) require.NoError(t, err) + var aClosed bool + defer func() { + if aClosed { + return + } + require.NoError(t, aAgent.Close()) + }() require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) disconnectedDuration := time.Second @@ -1519,6 +1599,13 @@ func TestLiteLifecycle(t *testing.T) { CheckInterval: &CheckInterval, }) require.NoError(t, err) + var bClosed bool + defer func() { + if bClosed { + return + } + require.NoError(t, bAgent.Close()) + }() bConnected := make(chan interface{}) bDisconnected := make(chan interface{}) @@ -1541,10 +1628,12 @@ func TestLiteLifecycle(t *testing.T) { <-aConnected <-bConnected require.NoError(t, aAgent.Close()) + aClosed = true <-bDisconnected <-bFailed require.NoError(t, bAgent.Close()) + bClosed = true } func TestNilCandidate(t *testing.T) { @@ -1558,9 +1647,11 @@ func TestNilCandidate(t *testing.T) { func TestNilCandidatePair(t *testing.T) { a, err := NewAgent(&AgentConfig{}) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() a.setSelectedPair(nil) - require.NoError(t, a.Close()) } func TestGetSelectedCandidatePair(t *testing.T) { @@ -1589,9 +1680,15 @@ func TestGetSelectedCandidatePair(t *testing.T) { aAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + require.NoError(t, aAgent.Close()) + }() bAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + require.NoError(t, bAgent.Close()) + }() aAgentPair, err := aAgent.GetSelectedCandidatePair() require.NoError(t, err) @@ -1615,8 +1712,6 @@ func TestGetSelectedCandidatePair(t *testing.T) { require.True(t, bAgentPair.Remote.Equal(aAgentPair.Local)) require.NoError(t, wan.Stop()) - require.NoError(t, aAgent.Close()) - require.NoError(t, bAgent.Close()) } func TestAcceptAggressiveNomination(t *testing.T) { @@ -1662,6 +1757,9 @@ func TestAcceptAggressiveNomination(t *testing.T) { var aAgent, bAgent *Agent aAgent, err = NewAgent(cfg0) require.NoError(t, err) + defer func() { + require.NoError(t, aAgent.Close()) + }() require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) cfg1 := &AgentConfig{ @@ -1674,9 +1772,12 @@ func TestAcceptAggressiveNomination(t *testing.T) { bAgent, err = NewAgent(cfg1) require.NoError(t, err) + defer func() { + require.NoError(t, bAgent.Close()) + }() require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) - aConn, bConn := connect(aAgent, bAgent) + connect(aAgent, bAgent) // Ensure pair selected // Note: this assumes ConnectionStateConnected is thrown after selecting the final pair @@ -1735,7 +1836,6 @@ func TestAcceptAggressiveNomination(t *testing.T) { } require.NoError(t, wan.Stop()) - closePipe(t, aConn, bConn) } // Close can deadlock but GracefulClose must not @@ -1748,15 +1848,29 @@ func TestAgentGracefulCloseDeadlock(t *testing.T) { } aAgent, err := NewAgent(config) require.NoError(t, err) + var aAgentClosed bool + defer func() { + if aAgentClosed { + return + } + require.NoError(t, aAgent.Close()) + }() bAgent, err := NewAgent(config) require.NoError(t, err) + var bAgentClosed bool + defer func() { + if bAgentClosed { + return + } + require.NoError(t, bAgent.Close()) + }() var connected, closeNow, closed sync.WaitGroup connected.Add(2) closeNow.Add(1) closed.Add(2) - closeHdlr := func(agent *Agent) { + closeHdlr := func(agent *Agent, agentClosed *bool) { check(agent.OnConnectionStateChange(func(cs ConnectionState) { if cs == ConnectionStateConnected { connected.Done() @@ -1766,14 +1880,15 @@ func TestAgentGracefulCloseDeadlock(t *testing.T) { if err := agent.GracefulClose(); err != nil { require.NoError(t, err) } + *agentClosed = true closed.Done() }() } })) } - closeHdlr(aAgent) - closeHdlr(bAgent) + closeHdlr(aAgent, &aAgentClosed) + closeHdlr(bAgent, &bAgentClosed) t.Log("connecting agents") _, _ = connect(aAgent, bAgent) @@ -1784,8 +1899,4 @@ func TestAgentGracefulCloseDeadlock(t *testing.T) { t.Log("tell them to close themselves in the same callback and wait") closeNow.Done() closed.Wait() - - // already closed - require.Error(t, aAgent.Close()) - require.Error(t, bAgent.Close()) } diff --git a/agent_udpmux_test.go b/agent_udpmux_test.go index 70e7d25..8f4efe3 100644 --- a/agent_udpmux_test.go +++ b/agent_udpmux_test.go @@ -50,12 +50,26 @@ func TestMuxAgent(t *testing.T) { IncludeLoopback: addr.IP.IsLoopback(), }) require.NoError(t, err) + var muxedAClosed bool + defer func() { + if muxedAClosed { + return + } + require.NoError(t, muxedA.Close()) + }() a, err := NewAgent(&AgentConfig{ CandidateTypes: []CandidateType{CandidateTypeHost}, NetworkTypes: supportedNetworkTypes(), }) require.NoError(t, err) + var aClosed bool + defer func() { + if aClosed { + return + } + require.NoError(t, a.Close()) + }() conn, muxedConn := connect(a, muxedA) @@ -83,7 +97,9 @@ func TestMuxAgent(t *testing.T) { // Close it down require.NoError(t, conn.Close()) + aClosed = true require.NoError(t, muxedConn.Close()) + muxedAClosed = true require.NoError(t, udpMux.Close()) // Expect error when reading from closed mux diff --git a/candidate_relay_test.go b/candidate_relay_test.go index b3ebc26..f74c0d5 100644 --- a/candidate_relay_test.go +++ b/candidate_relay_test.go @@ -43,6 +43,9 @@ func TestRelayOnlyConnection(t *testing.T) { }, }) require.NoError(t, err) + defer func() { + require.NoError(t, server.Close()) + }() cfg := &AgentConfig{ NetworkTypes: supportedNetworkTypes(), @@ -61,12 +64,18 @@ func TestRelayOnlyConnection(t *testing.T) { aAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + require.NoError(t, aAgent.Close()) + }() aNotifier, aConnected := onConnected() require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) bAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + require.NoError(t, bAgent.Close()) + }() bNotifier, bConnected := onConnected() require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) @@ -74,8 +83,4 @@ func TestRelayOnlyConnection(t *testing.T) { connect(aAgent, bAgent) <-aConnected <-bConnected - - require.NoError(t, aAgent.Close()) - require.NoError(t, bAgent.Close()) - require.NoError(t, server.Close()) } diff --git a/candidate_server_reflexive_test.go b/candidate_server_reflexive_test.go index 037c058..5453140 100644 --- a/candidate_server_reflexive_test.go +++ b/candidate_server_reflexive_test.go @@ -39,6 +39,9 @@ func TestServerReflexiveOnlyConnection(t *testing.T) { }, }) require.NoError(t, err) + defer func() { + require.NoError(t, server.Close()) + }() cfg := &AgentConfig{ NetworkTypes: []NetworkType{NetworkTypeUDP4}, @@ -54,12 +57,18 @@ func TestServerReflexiveOnlyConnection(t *testing.T) { aAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + require.NoError(t, aAgent.Close()) + }() aNotifier, aConnected := onConnected() require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) bAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + require.NoError(t, bAgent.Close()) + }() bNotifier, bConnected := onConnected() require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) @@ -67,8 +76,4 @@ func TestServerReflexiveOnlyConnection(t *testing.T) { connect(aAgent, bAgent) <-aConnected <-bConnected - - require.NoError(t, aAgent.Close()) - require.NoError(t, bAgent.Close()) - require.NoError(t, server.Close()) } diff --git a/connectivity_vnet_test.go b/connectivity_vnet_test.go index f85ac54..0c20e8c 100644 --- a/connectivity_vnet_test.go +++ b/connectivity_vnet_test.go @@ -493,6 +493,9 @@ func TestDisconnectedToConnected(t *testing.T) { CheckInterval: &keepaliveInterval, }) require.NoError(t, err) + defer func() { + require.NoError(t, controllingAgent.Close()) + }() controlledAgent, err := NewAgent(&AgentConfig{ NetworkTypes: supportedNetworkTypes(), @@ -503,6 +506,9 @@ func TestDisconnectedToConnected(t *testing.T) { CheckInterval: &keepaliveInterval, }) require.NoError(t, err) + defer func() { + require.NoError(t, controlledAgent.Close()) + }() controllingStateChanges := make(chan ConnectionState, 100) require.NoError(t, controllingAgent.OnConnectionStateChange(func(c ConnectionState) { @@ -538,8 +544,6 @@ func TestDisconnectedToConnected(t *testing.T) { blockUntilStateSeen(ConnectionStateConnected, controlledStateChanges) require.NoError(t, wan.Stop()) - require.NoError(t, controllingAgent.Close()) - require.NoError(t, controlledAgent.Close()) } // Agent.Write should use the best valid pair if a selected pair is not yet available @@ -593,6 +597,9 @@ func TestWriteUseValidPair(t *testing.T) { Net: net0, }) require.NoError(t, err) + defer func() { + require.NoError(t, controllingAgent.Close()) + }() controlledAgent, err := NewAgent(&AgentConfig{ NetworkTypes: supportedNetworkTypes(), @@ -600,6 +607,9 @@ func TestWriteUseValidPair(t *testing.T) { Net: net1, }) require.NoError(t, err) + defer func() { + require.NoError(t, controlledAgent.Close()) + }() gatherAndExchangeCandidates(controllingAgent, controlledAgent) @@ -630,6 +640,4 @@ func TestWriteUseValidPair(t *testing.T) { require.Equal(t, readBuf, testMessage) require.NoError(t, wan.Stop()) - require.NoError(t, controllingAgent.Close()) - require.NoError(t, controlledAgent.Close()) } diff --git a/gather_test.go b/gather_test.go index 1e4b896..8d54e2b 100644 --- a/gather_test.go +++ b/gather_test.go @@ -33,6 +33,9 @@ import ( func TestListenUDP(t *testing.T) { a, err := NewAgent(&AgentConfig{}) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() _, localAddrs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) require.NotEqual(t, len(localAddrs), 0, "localInterfaces found no interfaces, unable to test") @@ -84,8 +87,6 @@ func TestListenUDP(t *testing.T) { } _, err = listenUDPInPortRange(a.net, a.log, portMax, portMin, udp, &net.UDPAddr{IP: ip, Port: 0}) require.Equal(t, err, ErrPort, "listenUDP with port restriction [%d, %d], did not return ErrPort", portMin, portMax) - - require.NoError(t, a.Close()) } func TestGatherConcurrency(t *testing.T) { @@ -98,6 +99,9 @@ func TestGatherConcurrency(t *testing.T) { IncludeLoopback: true, }) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) require.NoError(t, a.OnCandidate(func(Candidate) { @@ -110,8 +114,6 @@ func TestGatherConcurrency(t *testing.T) { } <-candidateGathered.Done() - - require.NoError(t, a.Close()) } func TestLoopbackCandidate(t *testing.T) { @@ -194,6 +196,9 @@ func TestLoopbackCandidate(t *testing.T) { t.Run(tcase.name, func(t *testing.T) { a, err := NewAgent(tc.agentConfig) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) var loopback int32 @@ -212,7 +217,6 @@ func TestLoopbackCandidate(t *testing.T) { <-candidateGathered.Done() - require.NoError(t, a.Close()) require.Equal(t, tcase.loExpected, atomic.LoadInt32(&loopback) == 1) }) } @@ -243,6 +247,9 @@ func TestSTUNConcurrency(t *testing.T) { }, }) require.NoError(t, err) + defer func() { + require.NoError(t, server.Close()) + }() urls := []*stun.URI{} for i := 0; i <= 10; i++ { @@ -279,6 +286,9 @@ func TestSTUNConcurrency(t *testing.T) { ), }) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) require.NoError(t, a.OnCandidate(func(c Candidate) { @@ -291,9 +301,6 @@ func TestSTUNConcurrency(t *testing.T) { require.NoError(t, a.GatherCandidates()) <-candidateGathered.Done() - - require.NoError(t, a.Close()) - require.NoError(t, server.Close()) } // Assert that TURN gathering is done concurrently @@ -326,6 +333,9 @@ func TestTURNConcurrency(t *testing.T) { ListenerConfigs: listenerConfigs, }) require.NoError(t, err) + defer func() { + require.NoError(t, server.Close()) + }() urls := []*stun.URI{} for i := 0; i <= 10; i++ { @@ -354,6 +364,9 @@ func TestTURNConcurrency(t *testing.T) { Urls: urls, }) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) require.NoError(t, a.OnCandidate(func(c Candidate) { @@ -364,9 +377,6 @@ func TestTURNConcurrency(t *testing.T) { require.NoError(t, a.GatherCandidates()) <-candidateGathered.Done() - - require.NoError(t, a.Close()) - require.NoError(t, server.Close()) } t.Run("UDP Relay", func(t *testing.T) { @@ -433,6 +443,9 @@ func TestSTUNTURNConcurrency(t *testing.T) { }, }) require.NoError(t, err) + defer func() { + require.NoError(t, server.Close()) + }() urls := []*stun.URI{} for i := 0; i <= 10; i++ { @@ -457,6 +470,9 @@ func TestSTUNTURNConcurrency(t *testing.T) { CandidateTypes: []CandidateType{CandidateTypeServerReflexive, CandidateTypeRelay}, }) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() { gatherLim := test.TimeOut(time.Second * 3) // As TURN and STUN should be checked in parallel, this should complete before the default STUN timeout (5s) @@ -471,9 +487,6 @@ func TestSTUNTURNConcurrency(t *testing.T) { <-candidateGathered.Done() gatherLim.Stop() } - - require.NoError(t, a.Close()) - require.NoError(t, server.Close()) } // Assert that srflx candidates can be gathered from TURN servers @@ -502,6 +515,9 @@ func TestTURNSrflx(t *testing.T) { }, }) require.NoError(t, err) + defer func() { + require.NoError(t, server.Close()) + }() urls := []*stun.URI{{ Scheme: stun.SchemeTypeTURN, @@ -518,6 +534,9 @@ func TestTURNSrflx(t *testing.T) { CandidateTypes: []CandidateType{CandidateTypeServerReflexive, CandidateTypeRelay}, }) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) require.NoError(t, a.OnCandidate(func(c Candidate) { @@ -529,21 +548,19 @@ func TestTURNSrflx(t *testing.T) { require.NoError(t, a.GatherCandidates()) <-candidateGathered.Done() - - require.NoError(t, a.Close()) - require.NoError(t, server.Close()) } func TestCloseConnLog(t *testing.T) { a, err := NewAgent(&AgentConfig{}) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() closeConnAndLog(nil, a.log, "normal nil") var nc *net.UDPConn closeConnAndLog(nc, a.log, "nil ptr") - - require.NoError(t, a.Close()) } type mockProxy struct { @@ -598,6 +615,9 @@ func TestTURNProxyDialer(t *testing.T) { ProxyDialer: proxyDialer, }) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() candidateGatherFinish, candidateGatherFinishFunc := context.WithCancel(context.Background()) require.NoError(t, a.OnCandidate(func(c Candidate) { @@ -609,8 +629,6 @@ func TestTURNProxyDialer(t *testing.T) { require.NoError(t, a.GatherCandidates()) <-candidateGatherFinish.Done() <-proxyWasDialed.Done() - - require.NoError(t, a.Close()) } // TestUDPMuxDefaultWithNAT1To1IPsUsage requires that candidates @@ -639,6 +657,9 @@ func TestUDPMuxDefaultWithNAT1To1IPsUsage(t *testing.T) { UDPMux: mux, }) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() gatherCandidateDone := make(chan struct{}) require.NoError(t, a.OnCandidate(func(c Candidate) { @@ -652,8 +673,6 @@ func TestUDPMuxDefaultWithNAT1To1IPsUsage(t *testing.T) { <-gatherCandidateDone require.NotEqual(t, 0, len(mux.connsIPv4)) - - require.NoError(t, a.Close()) } // Assert that candidates are given for each mux in a MultiUDPMux @@ -687,6 +706,9 @@ func TestMultiUDPMuxUsage(t *testing.T) { UDPMux: NewMultiUDPMuxDefault(udpMuxInstances...), }) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() candidateCh := make(chan Candidate) require.NoError(t, a.OnCandidate(func(c Candidate) { @@ -707,8 +729,6 @@ func TestMultiUDPMuxUsage(t *testing.T) { for _, port := range expectedPorts { require.True(t, portFound[port], "There should be a candidate for each UDP mux port") } - - require.NoError(t, a.Close()) } // Assert that candidates are given for each mux in a MultiTCPMux @@ -743,6 +763,9 @@ func TestMultiTCPMuxUsage(t *testing.T) { TCPMux: NewMultiTCPMuxDefault(tcpMuxInstances...), }) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() candidateCh := make(chan Candidate) require.NoError(t, a.OnCandidate(func(c Candidate) { @@ -765,8 +788,6 @@ func TestMultiTCPMuxUsage(t *testing.T) { for _, port := range expectedPorts { require.True(t, portFound[port], "There should be a candidate for each TCP mux port") } - - require.NoError(t, a.Close()) } // Assert that UniversalUDPMux is used while gathering when configured in the Agent @@ -802,6 +823,13 @@ func TestUniversalUDPMuxUsage(t *testing.T) { UDPMuxSrflx: udpMuxSrflx, }) require.NoError(t, err) + var aClosed bool + defer func() { + if aClosed { + return + } + require.NoError(t, a.Close()) + }() candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) require.NoError(t, a.OnCandidate(func(c Candidate) { @@ -816,6 +844,8 @@ func TestUniversalUDPMuxUsage(t *testing.T) { <-candidateGathered.Done() require.NoError(t, a.Close()) + aClosed = true + // Twice because of 2 STUN servers configured require.Equal(t, numSTUNS, udpMuxSrflx.getXORMappedAddrUsedTimes, "expected times that GetXORMappedAddr should be called") // One for Restart() when agent has been initialized and one time when Close() the agent diff --git a/gather_vnet_test.go b/gather_vnet_test.go index 9fda3fd..15f9e3d 100644 --- a/gather_vnet_test.go +++ b/gather_vnet_test.go @@ -33,14 +33,15 @@ func TestVNetGather(t *testing.T) { Net: n, }) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() _, localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) if len(localIPs) > 0 { t.Fatal("should return no local IP") } require.NoError(t, err) - - require.NoError(t, a.Close()) }) t.Run("Gather a dynamic IP address", func(t *testing.T) { @@ -72,6 +73,9 @@ func TestVNetGather(t *testing.T) { Net: nw, }) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() _, localAddrs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) if len(localAddrs) == 0 { @@ -87,8 +91,6 @@ func TestVNetGather(t *testing.T) { t.Fatal("should be contained in the CIDR") } } - - require.NoError(t, a.Close()) }) t.Run("listenUDP", func(t *testing.T) { @@ -114,6 +116,9 @@ func TestVNetGather(t *testing.T) { if err != nil { t.Fatalf("Failed to create agent: %s", err) } + defer func() { + require.NoError(t, a.Close()) + }() _, localAddrs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) if len(localAddrs) == 0 { @@ -145,6 +150,9 @@ func TestVNetGather(t *testing.T) { } else if conn == nil { t.Fatalf("listenUDP error with no port restriction return a nil conn") } + defer func() { + require.NoError(t, conn.Close()) + }() _, port, err := net.SplitHostPort(conn.LocalAddr().String()) @@ -152,9 +160,6 @@ func TestVNetGather(t *testing.T) { if port != "5000" { t.Fatalf("listenUDP with port restriction of 5000 listened on incorrect port (%s)", port) } - - require.NoError(t, conn.Close()) - require.NoError(t, a.Close()) }) } @@ -209,7 +214,9 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { Net: nw, }) require.NoError(t, err, "should succeed") - defer a.Close() //nolint:errcheck + defer func() { + require.NoError(t, a.Close()) + }() done := make(chan struct{}) err = a.OnCandidate(func(c Candidate) { @@ -309,7 +316,9 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { Net: nw, }) require.NoError(t, err, "should succeed") - defer a.Close() //nolint:errcheck + defer func() { + require.NoError(t, a.Close()) + }() done := make(chan struct{}) err = a.OnCandidate(func(c Candidate) { @@ -384,6 +393,9 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { }, }) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() _, localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) require.NoError(t, err) @@ -391,8 +403,6 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { if len(localIPs) != 0 { t.Fatal("InterfaceFilter should have excluded everything") } - - require.NoError(t, a.Close()) }) t.Run("IPFilter should exclude the IP", func(t *testing.T) { @@ -404,6 +414,9 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { }, }) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() _, localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) require.NoError(t, err) @@ -411,8 +424,6 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { if len(localIPs) != 0 { t.Fatal("IPFilter should have excluded everything") } - - require.NoError(t, a.Close()) }) t.Run("InterfaceFilter should not exclude the interface", func(t *testing.T) { @@ -424,6 +435,9 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { }, }) require.NoError(t, err) + defer func() { + require.NoError(t, a.Close()) + }() _, localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) require.NoError(t, err) @@ -431,8 +445,6 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { if len(localIPs) == 0 { t.Fatal("InterfaceFilter should not have excluded anything") } - - require.NoError(t, a.Close()) }) } @@ -469,8 +481,10 @@ func TestVNetGather_TURNConnectionLeak(t *testing.T) { } aAgent, err := NewAgent(cfg0) require.NoError(t, err, "should succeed") + defer func() { + // Assert relay conn leak on close. + require.NoError(t, aAgent.Close()) + }() aAgent.gatherCandidatesRelay(context.Background(), []*stun.URI{turnServerURL}) - // Assert relay conn leak on close. - require.NoError(t, aAgent.Close()) } diff --git a/go.mod b/go.mod index 731e7cf..c122997 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/pion/mdns/v2 v2.0.7 github.com/pion/randutil v0.1.0 github.com/pion/stun/v2 v2.0.0 - github.com/pion/transport/v3 v3.0.5 + github.com/pion/transport/v3 v3.0.6 github.com/pion/turn/v3 v3.0.3 github.com/stretchr/testify v1.9.0 golang.org/x/net v0.26.0 diff --git a/go.sum b/go.sum index 22a1dc7..abaf650 100644 --- a/go.sum +++ b/go.sum @@ -23,8 +23,8 @@ github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1A github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo= github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= -github.com/pion/transport/v3 v3.0.5 h1:ofVrcbPNqVPuKaTO5AMFnFuJ1ZX7ElYiWzC5PCf9YVQ= -github.com/pion/transport/v3 v3.0.5/go.mod h1:HvJr2N/JwNJAfipsRleqwFoR3t/pWyHeZUs89v3+t5s= +github.com/pion/transport/v3 v3.0.6 h1:k1mQU06bmmX143qSWgXFqSH1KUJceQvIUuVH/K5ELWw= +github.com/pion/transport/v3 v3.0.6/go.mod h1:HvJr2N/JwNJAfipsRleqwFoR3t/pWyHeZUs89v3+t5s= github.com/pion/turn/v3 v3.0.3 h1:1e3GVk8gHZLPBA5LqadWYV60lmaKUaHCkm9DX9CkGcE= github.com/pion/turn/v3 v3.0.3/go.mod h1:vw0Dz420q7VYAF3J4wJKzReLHIo2LGp4ev8nXQexYsc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/mdns_test.go b/mdns_test.go index 617492b..a65d836 100644 --- a/mdns_test.go +++ b/mdns_test.go @@ -49,12 +49,18 @@ func TestMulticastDNSOnlyConnection(t *testing.T) { aAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + require.NoError(t, aAgent.Close()) + }() aNotifier, aConnected := onConnected() require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) bAgent, err := NewAgent(cfg) require.NoError(t, err) + defer func() { + require.NoError(t, bAgent.Close()) + }() bNotifier, bConnected := onConnected() require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) @@ -62,9 +68,6 @@ func TestMulticastDNSOnlyConnection(t *testing.T) { connect(aAgent, bAgent) <-aConnected <-bConnected - - require.NoError(t, aAgent.Close()) - require.NoError(t, bAgent.Close()) }) } } @@ -100,6 +103,9 @@ func TestMulticastDNSMixedConnection(t *testing.T) { InterfaceFilter: problematicNetworkInterfaces, }) require.NoError(t, err) + defer func() { + require.NoError(t, aAgent.Close()) + }() aNotifier, aConnected := onConnected() require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) @@ -111,6 +117,9 @@ func TestMulticastDNSMixedConnection(t *testing.T) { InterfaceFilter: problematicNetworkInterfaces, }) require.NoError(t, err) + defer func() { + require.NoError(t, bAgent.Close()) + }() bNotifier, bConnected := onConnected() require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) @@ -118,9 +127,6 @@ func TestMulticastDNSMixedConnection(t *testing.T) { connect(aAgent, bAgent) <-aConnected <-bConnected - - require.NoError(t, aAgent.Close()) - require.NoError(t, bAgent.Close()) }) } } @@ -165,6 +171,9 @@ func TestMulticastDNSStaticHostName(t *testing.T) { InterfaceFilter: problematicNetworkInterfaces, }) require.NoError(t, err) + defer func() { + require.NoError(t, agent.Close()) + }() correctHostName, resolveFunc := context.WithCancel(context.Background()) require.NoError(t, agent.OnCandidate(func(c Candidate) { @@ -175,7 +184,6 @@ func TestMulticastDNSStaticHostName(t *testing.T) { require.NoError(t, agent.GatherCandidates()) <-correctHostName.Done() - require.NoError(t, agent.Close()) }) } } diff --git a/transport_test.go b/transport_test.go index c7907a1..511d253 100644 --- a/transport_test.go +++ b/transport_test.go @@ -324,6 +324,7 @@ func TestConnStats(t *testing.T) { if _, err := ca.Write(make([]byte, 10)); err != nil { t.Fatal("unexpected error trying to write") } + defer closePipe(t, ca, cb) var wg sync.WaitGroup wg.Add(1) @@ -344,16 +345,4 @@ func TestConnStats(t *testing.T) { if cb.BytesReceived() != 10 { t.Fatal("bytes received don't match") } - - err := ca.Close() - if err != nil { - // We should never get here. - panic(err) - } - - err = cb.Close() - if err != nil { - // We should never get here. - panic(err) - } } diff --git a/transport_vnet_test.go b/transport_vnet_test.go index 36a22f5..1644742 100644 --- a/transport_vnet_test.go +++ b/transport_vnet_test.go @@ -61,6 +61,7 @@ func TestRemoteLocalAddr(t *testing.T) { urls: []*stun.URI{stunServerURL}, }, ) + defer closePipe(t, ca, cb) aRAddr := ca.RemoteAddr() aLAddr := ca.LocalAddr() @@ -86,9 +87,5 @@ func TestRemoteLocalAddr(t *testing.T) { require.Equal(t, bRAddr.String(), fmt.Sprintf("%s:%d", vnetGlobalIPA, aLAddr.(*net.UDPAddr).Port), //nolint:forcetypeassert ) - - // Close - require.NoError(t, ca.Close()) - require.NoError(t, cb.Close()) }) } diff --git a/udp_mux_test.go b/udp_mux_test.go index d25a070..1e218a8 100644 --- a/udp_mux_test.go +++ b/udp_mux_test.go @@ -250,6 +250,7 @@ func TestUDPMux_Agent_Restart(t *testing.T) { DisconnectedTimeout: &oneSecond, FailedTimeout: &oneSecond, }) + defer closePipe(t, connA, connB) aNotifier, aConnected := onConnected() require.NoError(t, connA.agent.OnConnectionStateChange(aNotifier)) @@ -277,7 +278,4 @@ func TestUDPMux_Agent_Restart(t *testing.T) { // Wait until both have gone back to connected <-aConnected <-bConnected - - require.NoError(t, connA.agent.Close()) - require.NoError(t, connB.agent.Close()) } From 978c2e6f47ae1ab9f7998ef2c5baf76a6996191f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 00:47:59 +0000 Subject: [PATCH 060/114] Update module golang.org/x/net to v0.27.0 Generated by renovateBot --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index c122997..3a351b3 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pion/transport/v3 v3.0.6 github.com/pion/turn/v3 v3.0.3 github.com/stretchr/testify v1.9.0 - golang.org/x/net v0.26.0 + golang.org/x/net v0.27.0 ) require ( @@ -21,7 +21,7 @@ require ( github.com/pion/transport/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/wlynxg/anet v0.0.3 // indirect - golang.org/x/crypto v0.24.0 // indirect + golang.org/x/crypto v0.25.0 // indirect golang.org/x/sys v0.22.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index abaf650..06be9ef 100644 --- a/go.sum +++ b/go.sum @@ -46,8 +46,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -58,8 +58,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From 5e36528411635a563c0b0f2ed5370a2469d1f77e Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Fri, 2 Aug 2024 11:34:02 -0400 Subject: [PATCH 061/114] fix missed graceful close event on notifier --- agent_handlers.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/agent_handlers.go b/agent_handlers.go index 3de9f32..0c02277 100644 --- a/agent_handlers.go +++ b/agent_handlers.go @@ -62,6 +62,12 @@ type handlerNotifier struct { } func (h *handlerNotifier) Close(graceful bool) { + if graceful { + // if we were closed ungracefully before, we now + // want ot wait. + defer h.notifiers.Wait() + } + h.Lock() select { @@ -72,10 +78,6 @@ func (h *handlerNotifier) Close(graceful bool) { } close(h.done) h.Unlock() - - if graceful { - h.notifiers.Wait() - } } func (h *handlerNotifier) EnqueueConnectionState(s ConnectionState) { From 1df10649e397d9351be689859915f214853ee4cd Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Fri, 2 Aug 2024 12:08:58 -0400 Subject: [PATCH 062/114] Bump transport to v3.0.7 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 3a351b3..e9ba129 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/pion/mdns/v2 v2.0.7 github.com/pion/randutil v0.1.0 github.com/pion/stun/v2 v2.0.0 - github.com/pion/transport/v3 v3.0.6 + github.com/pion/transport/v3 v3.0.7 github.com/pion/turn/v3 v3.0.3 github.com/stretchr/testify v1.9.0 golang.org/x/net v0.27.0 diff --git a/go.sum b/go.sum index 06be9ef..c781bdf 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,8 @@ github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLh github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= github.com/pion/transport/v3 v3.0.6 h1:k1mQU06bmmX143qSWgXFqSH1KUJceQvIUuVH/K5ELWw= github.com/pion/transport/v3 v3.0.6/go.mod h1:HvJr2N/JwNJAfipsRleqwFoR3t/pWyHeZUs89v3+t5s= +github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= +github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= github.com/pion/turn/v3 v3.0.3 h1:1e3GVk8gHZLPBA5LqadWYV60lmaKUaHCkm9DX9CkGcE= github.com/pion/turn/v3 v3.0.3/go.mod h1:vw0Dz420q7VYAF3J4wJKzReLHIo2LGp4ev8nXQexYsc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= From 63ede543de9fea6e8bfe673b70a02e12a1fec166 Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Fri, 2 Aug 2024 12:11:31 -0400 Subject: [PATCH 063/114] go mod tidy --- go.sum | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.sum b/go.sum index c781bdf..446e720 100644 --- a/go.sum +++ b/go.sum @@ -23,8 +23,6 @@ github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1A github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo= github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= -github.com/pion/transport/v3 v3.0.6 h1:k1mQU06bmmX143qSWgXFqSH1KUJceQvIUuVH/K5ELWw= -github.com/pion/transport/v3 v3.0.6/go.mod h1:HvJr2N/JwNJAfipsRleqwFoR3t/pWyHeZUs89v3+t5s= github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= github.com/pion/turn/v3 v3.0.3 h1:1e3GVk8gHZLPBA5LqadWYV60lmaKUaHCkm9DX9CkGcE= From 28cf1cd9f3f973403140e26a985f27903aee7a5e Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Mon, 5 Aug 2024 11:38:40 -0400 Subject: [PATCH 064/114] Allow multiple agent.Close --- agent.go | 4 ++-- internal/taskloop/taskloop.go | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/agent.go b/agent.go index f8ee66e..5516484 100644 --- a/agent.go +++ b/agent.go @@ -861,14 +861,14 @@ func (a *Agent) GracefulClose() error { func (a *Agent) close(graceful bool) error { // the loop is safe to wait on no matter what - err := a.loop.Close() + a.loop.Close() // but we are in less control of the notifiers, so we will // pass through `graceful`. a.connectionStateNotifier.Close(graceful) a.candidateNotifier.Close(graceful) a.selectedCandidatePairNotifier.Close(graceful) - return err + return nil } // Remove all candidates. This closes any listening sockets diff --git a/internal/taskloop/taskloop.go b/internal/taskloop/taskloop.go index b850ab4..6e70efd 100644 --- a/internal/taskloop/taskloop.go +++ b/internal/taskloop/taskloop.go @@ -63,17 +63,15 @@ func (l *Loop) runLoop(onClose func()) { // Close stops the loop after finishing the execution of the current task. // Other pending tasks will not be executed. -func (l *Loop) Close() error { +func (l *Loop) Close() { if err := l.Err(); err != nil { - return err + return } l.err.Store(ErrClosed) close(l.done) <-l.taskLoopDone - - return nil } // Run serially executes the submitted callback. From 39c90d8419a4798b0c6a007e1e9ea767f2520db7 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Mon, 12 Aug 2024 11:36:11 -0400 Subject: [PATCH 065/114] Upgrade dtls, turn and sturn New major version of dtls causes API breaks on three packages --- agent.go | 6 +-- agent_config.go | 2 +- agent_test.go | 4 +- candidate_base.go | 2 +- candidate_relay_test.go | 4 +- candidate_server_reflexive_test.go | 4 +- candidatepair.go | 2 +- connectivity_vnet_test.go | 4 +- errors.go | 2 +- examples/ping-pong/main.go | 2 +- gather.go | 17 ++++--- gather_test.go | 8 +-- gather_vnet_test.go | 2 +- go.mod | 13 +++-- go.sum | 82 ++++-------------------------- icecontrol.go | 2 +- icecontrol_test.go | 2 +- internal/stun/stun.go | 2 +- internal/taskloop/taskloop.go | 2 +- priority.go | 2 +- priority_test.go | 2 +- selection.go | 2 +- selection_test.go | 2 +- tcp_mux.go | 2 +- tcp_mux_multi_test.go | 2 +- tcp_mux_test.go | 2 +- transport.go | 2 +- transport_test.go | 2 +- transport_vnet_test.go | 2 +- udp_mux.go | 2 +- udp_mux_test.go | 2 +- udp_mux_universal.go | 2 +- udp_mux_universal_test.go | 2 +- url.go | 2 +- usecandidate.go | 2 +- usecandidate_test.go | 2 +- 36 files changed, 69 insertions(+), 127 deletions(-) diff --git a/agent.go b/agent.go index 5516484..a574dd1 100644 --- a/agent.go +++ b/agent.go @@ -16,11 +16,11 @@ import ( "sync/atomic" "time" - stunx "github.com/pion/ice/v3/internal/stun" - "github.com/pion/ice/v3/internal/taskloop" + stunx "github.com/pion/ice/v4/internal/stun" + "github.com/pion/ice/v4/internal/taskloop" "github.com/pion/logging" "github.com/pion/mdns/v2" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/pion/transport/v3" "github.com/pion/transport/v3/packetio" "github.com/pion/transport/v3/stdnet" diff --git a/agent_config.go b/agent_config.go index 4c87fa1..562d8f3 100644 --- a/agent_config.go +++ b/agent_config.go @@ -8,7 +8,7 @@ import ( "time" "github.com/pion/logging" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/pion/transport/v3" "golang.org/x/net/proxy" ) diff --git a/agent_test.go b/agent_test.go index 401a7f1..b53c7d0 100644 --- a/agent_test.go +++ b/agent_test.go @@ -15,9 +15,9 @@ import ( "testing" "time" - "github.com/pion/ice/v3/internal/fakenet" + "github.com/pion/ice/v4/internal/fakenet" "github.com/pion/logging" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/pion/transport/v3/test" "github.com/pion/transport/v3/vnet" "github.com/stretchr/testify/require" diff --git a/candidate_base.go b/candidate_base.go index e1fbfe1..7d06368 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -15,7 +15,7 @@ import ( "sync/atomic" "time" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" ) type candidateBase struct { diff --git a/candidate_relay_test.go b/candidate_relay_test.go index f74c0d5..f22af07 100644 --- a/candidate_relay_test.go +++ b/candidate_relay_test.go @@ -12,9 +12,9 @@ import ( "testing" "time" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/pion/transport/v3/test" - "github.com/pion/turn/v3" + "github.com/pion/turn/v4" "github.com/stretchr/testify/require" ) diff --git a/candidate_server_reflexive_test.go b/candidate_server_reflexive_test.go index 5453140..77d344a 100644 --- a/candidate_server_reflexive_test.go +++ b/candidate_server_reflexive_test.go @@ -12,9 +12,9 @@ import ( "testing" "time" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/pion/transport/v3/test" - "github.com/pion/turn/v3" + "github.com/pion/turn/v4" "github.com/stretchr/testify/require" ) diff --git a/candidatepair.go b/candidatepair.go index 93470fe..7bbcdd7 100644 --- a/candidatepair.go +++ b/candidatepair.go @@ -6,7 +6,7 @@ package ice import ( "fmt" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" ) func newCandidatePair(local, remote Candidate, controlling bool) *CandidatePair { diff --git a/connectivity_vnet_test.go b/connectivity_vnet_test.go index 0c20e8c..6a13269 100644 --- a/connectivity_vnet_test.go +++ b/connectivity_vnet_test.go @@ -15,10 +15,10 @@ import ( "time" "github.com/pion/logging" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/pion/transport/v3/test" "github.com/pion/transport/v3/vnet" - "github.com/pion/turn/v3" + "github.com/pion/turn/v4" "github.com/stretchr/testify/require" ) diff --git a/errors.go b/errors.go index e9bfe07..02736cc 100644 --- a/errors.go +++ b/errors.go @@ -6,7 +6,7 @@ package ice import ( "errors" - "github.com/pion/ice/v3/internal/taskloop" + "github.com/pion/ice/v4/internal/taskloop" ) var ( diff --git a/examples/ping-pong/main.go b/examples/ping-pong/main.go index 1d9574d..a8117d9 100644 --- a/examples/ping-pong/main.go +++ b/examples/ping-pong/main.go @@ -14,7 +14,7 @@ import ( "os" "time" - "github.com/pion/ice/v3" + "github.com/pion/ice/v4" "github.com/pion/randutil" ) diff --git a/gather.go b/gather.go index ec6831c..99b5c06 100644 --- a/gather.go +++ b/gather.go @@ -13,12 +13,12 @@ import ( "reflect" "sync" - "github.com/pion/dtls/v2" - "github.com/pion/ice/v3/internal/fakenet" - stunx "github.com/pion/ice/v3/internal/stun" + "github.com/pion/dtls/v3" + "github.com/pion/ice/v4/internal/fakenet" + stunx "github.com/pion/ice/v4/internal/stun" "github.com/pion/logging" - "github.com/pion/stun/v2" - "github.com/pion/turn/v3" + "github.com/pion/stun/v3" + "github.com/pion/turn/v4" ) // Close a net.Conn and log if we have a failure @@ -680,7 +680,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / return } - conn, connectErr := dtls.ClientWithContext(ctx, udpConn, &dtls.Config{ + conn, connectErr := dtls.Client(&fakenet.PacketConn{Conn: udpConn}, udpConn.RemoteAddr(), &dtls.Config{ ServerName: url.Host, InsecureSkipVerify: a.insecureSkipVerify, //nolint:gosec }) @@ -689,6 +689,11 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / return } + if connectErr = conn.HandshakeContext(ctx); connectErr != nil { + a.log.Warnf("Failed to create DTLS client: %v", turnServerAddr, connectErr) + return + } + relAddr = conn.LocalAddr().(*net.UDPAddr).IP.String() //nolint:forcetypeassert relPort = conn.LocalAddr().(*net.UDPAddr).Port //nolint:forcetypeassert relayProtocol = relayProtocolDTLS diff --git a/gather_test.go b/gather_test.go index 8d54e2b..802f637 100644 --- a/gather_test.go +++ b/gather_test.go @@ -20,12 +20,12 @@ import ( "testing" "time" - "github.com/pion/dtls/v2" - "github.com/pion/dtls/v2/pkg/crypto/selfsign" + "github.com/pion/dtls/v3" + "github.com/pion/dtls/v3/pkg/crypto/selfsign" "github.com/pion/logging" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/pion/transport/v3/test" - "github.com/pion/turn/v3" + "github.com/pion/turn/v4" "github.com/stretchr/testify/require" "golang.org/x/net/proxy" ) diff --git a/gather_vnet_test.go b/gather_vnet_test.go index 15f9e3d..3babce3 100644 --- a/gather_vnet_test.go +++ b/gather_vnet_test.go @@ -14,7 +14,7 @@ import ( "testing" "github.com/pion/logging" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/pion/transport/v3/test" "github.com/pion/transport/v3/vnet" "github.com/stretchr/testify/require" diff --git a/go.mod b/go.mod index e9ba129..b108cc8 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,16 @@ -module github.com/pion/ice/v3 +module github.com/pion/ice/v4 go 1.19 require ( github.com/google/uuid v1.6.0 - github.com/pion/dtls/v2 v2.2.12 + github.com/pion/dtls/v3 v3.0.1 github.com/pion/logging v0.2.2 github.com/pion/mdns/v2 v2.0.7 github.com/pion/randutil v0.1.0 - github.com/pion/stun/v2 v2.0.0 + github.com/pion/stun/v3 v3.0.0 github.com/pion/transport/v3 v3.0.7 - github.com/pion/turn/v3 v3.0.3 + github.com/pion/turn/v4 v4.0.0 github.com/stretchr/testify v1.9.0 golang.org/x/net v0.27.0 ) @@ -18,11 +18,10 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/kr/pretty v0.1.0 // indirect - github.com/pion/transport/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/wlynxg/anet v0.0.3 // indirect - golang.org/x/crypto v0.25.0 // indirect - golang.org/x/sys v0.22.0 // indirect + golang.org/x/crypto v0.26.0 // indirect + golang.org/x/sys v0.24.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 446e720..96be52a 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,3 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -8,95 +7,34 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= -github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= -github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/dtls/v3 v3.0.1 h1:0kmoaPYLAo0md/VemjcrAXQiSf8U+tuU3nDYVNpEKaw= +github.com/pion/dtls/v3 v3.0.1/go.mod h1:dfIXcFkKoujDQ+jtd8M6RgqKK3DuaUilm3YatAbGp5k= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= -github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= -github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= -github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= -github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo= -github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= -github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= +github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw= +github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= -github.com/pion/turn/v3 v3.0.3 h1:1e3GVk8gHZLPBA5LqadWYV60lmaKUaHCkm9DX9CkGcE= -github.com/pion/turn/v3 v3.0.3/go.mod h1:vw0Dz420q7VYAF3J4wJKzReLHIo2LGp4ev8nXQexYsc= +github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM= +github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg= github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/icecontrol.go b/icecontrol.go index 82ed098..922f79a 100644 --- a/icecontrol.go +++ b/icecontrol.go @@ -6,7 +6,7 @@ package ice import ( "encoding/binary" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" ) // tiebreaker is common helper for ICE-{CONTROLLED,CONTROLLING} diff --git a/icecontrol_test.go b/icecontrol_test.go index 80a22de..a0a57bc 100644 --- a/icecontrol_test.go +++ b/icecontrol_test.go @@ -7,7 +7,7 @@ import ( "errors" "testing" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" ) func TestControlled_GetFrom(t *testing.T) { //nolint:dupl diff --git a/internal/stun/stun.go b/internal/stun/stun.go index 2b05f50..60379eb 100644 --- a/internal/stun/stun.go +++ b/internal/stun/stun.go @@ -10,7 +10,7 @@ import ( "net" "time" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" ) var ( diff --git a/internal/taskloop/taskloop.go b/internal/taskloop/taskloop.go index 6e70efd..d025998 100644 --- a/internal/taskloop/taskloop.go +++ b/internal/taskloop/taskloop.go @@ -10,7 +10,7 @@ import ( "errors" "time" - atomicx "github.com/pion/ice/v3/internal/atomic" + atomicx "github.com/pion/ice/v4/internal/atomic" ) // ErrClosed indicates that the loop has been stopped diff --git a/priority.go b/priority.go index 13689fb..f49df7f 100644 --- a/priority.go +++ b/priority.go @@ -6,7 +6,7 @@ package ice import ( "encoding/binary" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" ) // PriorityAttr represents PRIORITY attribute. diff --git a/priority_test.go b/priority_test.go index ea76272..82694b4 100644 --- a/priority_test.go +++ b/priority_test.go @@ -7,7 +7,7 @@ import ( "errors" "testing" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" ) func TestPriority_GetFrom(t *testing.T) { //nolint:dupl diff --git a/selection.go b/selection.go index 09a0988..f386d51 100644 --- a/selection.go +++ b/selection.go @@ -8,7 +8,7 @@ import ( "time" "github.com/pion/logging" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" ) type pairCandidateSelector interface { diff --git a/selection_test.go b/selection_test.go index a5e17fc..1c9a56a 100644 --- a/selection_test.go +++ b/selection_test.go @@ -16,7 +16,7 @@ import ( "testing" "time" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/pion/transport/v3/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/tcp_mux.go b/tcp_mux.go index dfedad1..ef6f038 100644 --- a/tcp_mux.go +++ b/tcp_mux.go @@ -13,7 +13,7 @@ import ( "time" "github.com/pion/logging" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" ) // ErrGetTransportAddress can't convert net.Addr to underlying type (UDPAddr or TCPAddr). diff --git a/tcp_mux_multi_test.go b/tcp_mux_multi_test.go index 5630689..619b857 100644 --- a/tcp_mux_multi_test.go +++ b/tcp_mux_multi_test.go @@ -12,7 +12,7 @@ import ( "testing" "github.com/pion/logging" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/pion/transport/v3/test" "github.com/stretchr/testify/require" ) diff --git a/tcp_mux_test.go b/tcp_mux_test.go index dc8dd8e..53fa9ee 100644 --- a/tcp_mux_test.go +++ b/tcp_mux_test.go @@ -11,7 +11,7 @@ import ( "time" "github.com/pion/logging" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/pion/transport/v3/test" "github.com/stretchr/testify/require" ) diff --git a/transport.go b/transport.go index f800152..cf9e3f9 100644 --- a/transport.go +++ b/transport.go @@ -9,7 +9,7 @@ import ( "sync/atomic" "time" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" ) // Dial connects to the remote agent, acting as the controlling ice agent. diff --git a/transport_test.go b/transport_test.go index 511d253..4563e57 100644 --- a/transport_test.go +++ b/transport_test.go @@ -14,7 +14,7 @@ import ( "testing" "time" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/pion/transport/v3/test" "github.com/stretchr/testify/require" ) diff --git a/transport_vnet_test.go b/transport_vnet_test.go index 1644742..bc8a4f6 100644 --- a/transport_vnet_test.go +++ b/transport_vnet_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/pion/transport/v3/test" "github.com/pion/transport/v3/vnet" "github.com/stretchr/testify/require" diff --git a/udp_mux.go b/udp_mux.go index 46f81ca..96197b3 100644 --- a/udp_mux.go +++ b/udp_mux.go @@ -13,7 +13,7 @@ import ( "sync" "github.com/pion/logging" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/pion/transport/v3" "github.com/pion/transport/v3/stdnet" ) diff --git a/udp_mux_test.go b/udp_mux_test.go index 1e218a8..d2c14d7 100644 --- a/udp_mux_test.go +++ b/udp_mux_test.go @@ -15,7 +15,7 @@ import ( "testing" "time" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/pion/transport/v3/test" "github.com/stretchr/testify/require" ) diff --git a/udp_mux_universal.go b/udp_mux_universal.go index e7d9004..79c12fb 100644 --- a/udp_mux_universal.go +++ b/udp_mux_universal.go @@ -9,7 +9,7 @@ import ( "time" "github.com/pion/logging" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/pion/transport/v3" ) diff --git a/udp_mux_universal_test.go b/udp_mux_universal_test.go index ec8de2e..3bf14ce 100644 --- a/udp_mux_universal_test.go +++ b/udp_mux_universal_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" "github.com/stretchr/testify/require" ) diff --git a/url.go b/url.go index 50d354a..29338bf 100644 --- a/url.go +++ b/url.go @@ -3,7 +3,7 @@ package ice -import "github.com/pion/stun/v2" +import "github.com/pion/stun/v3" type ( // URL represents a STUN (rfc7064) or TURN (rfc7065) URI diff --git a/usecandidate.go b/usecandidate.go index b5c489a..ea03bb8 100644 --- a/usecandidate.go +++ b/usecandidate.go @@ -3,7 +3,7 @@ package ice -import "github.com/pion/stun/v2" +import "github.com/pion/stun/v3" // UseCandidateAttr represents USE-CANDIDATE attribute. type UseCandidateAttr struct{} diff --git a/usecandidate_test.go b/usecandidate_test.go index 1dc48ee..c44409c 100644 --- a/usecandidate_test.go +++ b/usecandidate_test.go @@ -6,7 +6,7 @@ package ice import ( "testing" - "github.com/pion/stun/v2" + "github.com/pion/stun/v3" ) func TestUseCandidateAttr_AddTo(t *testing.T) { From 3051b4aaf0ed9151caf9c01d329053ff0bd7c127 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Mon, 12 Aug 2024 11:44:58 -0400 Subject: [PATCH 066/114] Fix pkg.go.dev link New major version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3035594..14e89ad 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Slack Widget
GitHub Workflow Status - Go Reference + Go Reference Coverage Status Go Report Card License: MIT From 5dfb91f200f8fdbcad961b98942b9815f41fd7ca Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Wed, 14 Aug 2024 13:50:31 -0400 Subject: [PATCH 067/114] Add test that NetworkTypes is respected Assert that a remote Passive TCP candidate doesn't cause a TCP connection to be started. Resolves pion/webrtc#2782 --- active_tcp_test.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/active_tcp_test.go b/active_tcp_test.go index fc51844..ee8d172 100644 --- a/active_tcp_test.go +++ b/active_tcp_test.go @@ -7,8 +7,10 @@ package ice import ( + "fmt" "net" "net/netip" + "sync/atomic" "testing" "time" @@ -216,3 +218,67 @@ func TestActiveTCP_NonBlocking(t *testing.T) { <-isConnected } + +// Assert that we ignore remote TCP candidates when running a UDP Only Agent +func TestActiveTCP_Respect_NetworkTypes(t *testing.T) { + defer test.CheckRoutines(t)() + defer test.TimeOut(time.Second * 5).Stop() + + tcpListener, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + + _, port, err := net.SplitHostPort(tcpListener.Addr().String()) + require.NoError(t, err) + + var incomingTCPCount uint64 + go func() { + for { + conn, listenErr := tcpListener.Accept() + if listenErr != nil { + return + } + + require.NoError(t, conn.Close()) + atomic.AddUint64(&incomingTCPCount, ^uint64(0)) + } + }() + + cfg := &AgentConfig{ + NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}, + InterfaceFilter: problematicNetworkInterfaces, + IncludeLoopback: true, + } + + aAgent, err := NewAgent(cfg) + require.NoError(t, err) + + defer func() { + require.NoError(t, aAgent.Close()) + }() + + bAgent, err := NewAgent(cfg) + require.NoError(t, err) + + defer func() { + require.NoError(t, bAgent.Close()) + }() + + isConnected := make(chan interface{}) + err = aAgent.OnConnectionStateChange(func(c ConnectionState) { + if c == ConnectionStateConnected { + close(isConnected) + } + }) + require.NoError(t, err) + + invalidCandidate, err := UnmarshalCandidate(fmt.Sprintf("1052353102 1 tcp 1675624447 127.0.0.1 %s typ host tcptype passive", port)) + require.NoError(t, err) + require.NoError(t, aAgent.AddRemoteCandidate(invalidCandidate)) + require.NoError(t, bAgent.AddRemoteCandidate(invalidCandidate)) + + connect(aAgent, bAgent) + + <-isConnected + require.NoError(t, tcpListener.Close()) + require.Equal(t, uint64(0), atomic.LoadUint64(&incomingTCPCount)) +} From 5d9b189feb9a8eced074a27931c1c3647766a3c9 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Wed, 14 Aug 2024 14:50:58 -0400 Subject: [PATCH 068/114] Take TCP Family into account before connecting Before if a user disabled TCPv6 (but enabled TCPv4) we would incorrectly start TCP connections over TCPv6 still. Resolves pion/webrtc#2782 --- active_tcp_test.go | 2 +- agent.go | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/active_tcp_test.go b/active_tcp_test.go index ee8d172..1e0cb0f 100644 --- a/active_tcp_test.go +++ b/active_tcp_test.go @@ -244,7 +244,7 @@ func TestActiveTCP_Respect_NetworkTypes(t *testing.T) { }() cfg := &AgentConfig{ - NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}, + NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6, NetworkTypeTCP6}, InterfaceFilter: problematicNetworkInterfaces, IncludeLoopback: true, } diff --git a/agent.go b/agent.go index a574dd1..fad036e 100644 --- a/agent.go +++ b/agent.go @@ -704,14 +704,17 @@ func (a *Agent) addRemoteCandidate(c Candidate) { } } - tcpNetworkTypeFound := false - for _, networkType := range a.networkTypes { - if networkType.IsTCP() { - tcpNetworkTypeFound = true + acceptRemotePassiveTCPCandidate := false + // Assert that TCP4 or TCP6 is a enabled NetworkType locally + if !a.disableActiveTCP && c.TCPType() == TCPTypePassive { + for _, networkType := range a.networkTypes { + if c.NetworkType() == networkType { + acceptRemotePassiveTCPCandidate = true + } } } - if !a.disableActiveTCP && tcpNetworkTypeFound && c.TCPType() == TCPTypePassive { + if acceptRemotePassiveTCPCandidate { a.addRemotePassiveTCPCandidate(c) } From bf68674e63b3e948d060382ac7b2a862d3742840 Mon Sep 17 00:00:00 2001 From: Pion <59523206+pionbot@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:16:53 +0000 Subject: [PATCH 069/114] Update CI configs to v0.11.15 Update lint scripts and CI configs. --- .github/workflows/test.yaml | 6 +++--- .golangci.yml | 8 ++++---- agent.go | 2 +- agent_config.go | 2 +- candidatepair.go | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 08e4272..b024289 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -23,7 +23,7 @@ jobs: uses: pion/.goassets/.github/workflows/test.reusable.yml@master strategy: matrix: - go: ["1.22", "1.21"] # auto-update/supported-go-version-list + go: ["1.23", "1.22"] # auto-update/supported-go-version-list fail-fast: false with: go-version: ${{ matrix.go }} @@ -33,7 +33,7 @@ jobs: uses: pion/.goassets/.github/workflows/test-i386.reusable.yml@master strategy: matrix: - go: ["1.22", "1.21"] # auto-update/supported-go-version-list + go: ["1.23", "1.22"] # auto-update/supported-go-version-list fail-fast: false with: go-version: ${{ matrix.go }} @@ -41,5 +41,5 @@ jobs: test-wasm: uses: pion/.goassets/.github/workflows/test-wasm.reusable.yml@master with: - go-version: "1.22" # auto-update/latest-go-version + go-version: "1.23" # auto-update/latest-go-version secrets: inherit diff --git a/.golangci.yml b/.golangci.yml index e06de4d..a3235be 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,6 +1,9 @@ # SPDX-FileCopyrightText: 2023 The Pion community # SPDX-License-Identifier: MIT +run: + timeout: 5m + linters-settings: govet: enable: @@ -48,7 +51,7 @@ linters: - goconst # Finds repeated strings that could be replaced by a constant - gocritic # The most opinionated Go source code linter - godox # Tool for detection of FIXME, TODO and other comment keywords - - goerr113 # Golang linter to check the errors handling expressions + - err113 # Golang linter to check the errors handling expressions - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern @@ -83,17 +86,14 @@ linters: - depguard # Go linter that checks if package imports are in a list of acceptable packages - containedctx # containedctx is a linter that detects struct contained context.Context field - cyclop # checks function and package cyclomatic complexity - - exhaustivestruct # Checks if all struct's fields are initialized - funlen # Tool for detection of long functions - gocyclo # Computes and checks the cyclomatic complexity of functions - godot # Check if comments end in a period - gomnd # An analyzer to detect magic numbers. - - ifshort # Checks that your code uses short syntax for if-statements whenever possible - ireturn # Accept Interfaces, Return Concrete Types - lll # Reports long lines - maintidx # maintidx measures the maintainability index of each function. - makezero # Finds slice declarations with non-zero initial length - - maligned # Tool to detect Go structs that would take less memory if their fields were sorted - nakedret # Finds naked returns in functions greater than a specified function length - nestif # Reports deeply nested if statements - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity diff --git a/agent.go b/agent.go index fad036e..c7970a5 100644 --- a/agent.go +++ b/agent.go @@ -267,7 +267,7 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit return nil, ErrLiteUsingNonHostCandidates } - if config.Urls != nil && len(config.Urls) > 0 && !containsCandidateType(CandidateTypeServerReflexive, a.candidateTypes) && !containsCandidateType(CandidateTypeRelay, a.candidateTypes) { + if len(config.Urls) > 0 && !containsCandidateType(CandidateTypeServerReflexive, a.candidateTypes) && !containsCandidateType(CandidateTypeRelay, a.candidateTypes) { a.closeMulticastConn() return nil, ErrUselessUrlsProvided } diff --git a/agent_config.go b/agent_config.go index 562d8f3..93e8889 100644 --- a/agent_config.go +++ b/agent_config.go @@ -270,7 +270,7 @@ func (config *AgentConfig) initWithDefaults(a *Agent) { a.checkInterval = *config.CheckInterval } - if config.CandidateTypes == nil || len(config.CandidateTypes) == 0 { + if len(config.CandidateTypes) == 0 { a.candidateTypes = defaultCandidateTypes() } else { a.candidateTypes = config.CandidateTypes diff --git a/candidatepair.go b/candidatepair.go index 7bbcdd7..2139209 100644 --- a/candidatepair.go +++ b/candidatepair.go @@ -66,13 +66,13 @@ func (p *CandidatePair) priority() uint64 { // Just implement these here rather // than fooling around with the math package - min := func(x, y uint32) uint64 { + localMin := func(x, y uint32) uint64 { if x < y { return uint64(x) } return uint64(y) } - max := func(x, y uint32) uint64 { + localMax := func(x, y uint32) uint64 { if x > y { return uint64(x) } @@ -87,7 +87,7 @@ func (p *CandidatePair) priority() uint64 { // 1<<32 overflows uint32; and if both g && d are // maxUint32, this result would overflow uint64 - return (1<<32-1)*min(g, d) + 2*max(g, d) + cmp(g, d) + return (1<<32-1)*localMin(g, d) + 2*localMax(g, d) + cmp(g, d) } func (p *CandidatePair) Write(b []byte) (int, error) { From 19b596b87f7e3addaea7742617ba4f3496ac7334 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 19:52:15 +0000 Subject: [PATCH 070/114] Update module github.com/pion/dtls/v3 to v3.0.2 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b108cc8..2a8d96b 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/google/uuid v1.6.0 - github.com/pion/dtls/v3 v3.0.1 + github.com/pion/dtls/v3 v3.0.2 github.com/pion/logging v0.2.2 github.com/pion/mdns/v2 v2.0.7 github.com/pion/randutil v0.1.0 diff --git a/go.sum b/go.sum index 96be52a..e8d86fa 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pion/dtls/v3 v3.0.1 h1:0kmoaPYLAo0md/VemjcrAXQiSf8U+tuU3nDYVNpEKaw= -github.com/pion/dtls/v3 v3.0.1/go.mod h1:dfIXcFkKoujDQ+jtd8M6RgqKK3DuaUilm3YatAbGp5k= +github.com/pion/dtls/v3 v3.0.2 h1:425DEeJ/jfuTTghhUDW0GtYZYIwwMtnKKJNMcWccTX0= +github.com/pion/dtls/v3 v3.0.2/go.mod h1:dfIXcFkKoujDQ+jtd8M6RgqKK3DuaUilm3YatAbGp5k= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= From 9a25a32f1b9cf60d6141697983af3f67719fffd5 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Mon, 26 Aug 2024 11:28:51 -0400 Subject: [PATCH 071/114] Update go.mod version to 1.20 Relates to pion/webrtc#2869 --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 2a8d96b..1c4ccd4 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/pion/ice/v4 -go 1.19 +go 1.20 require ( github.com/google/uuid v1.6.0 From 277014ea45bd08b0d34d56ba042e35e6dc090b2c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 00:05:44 +0000 Subject: [PATCH 072/114] Update module golang.org/x/net to v0.28.0 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1c4ccd4..8bd675f 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pion/transport/v3 v3.0.7 github.com/pion/turn/v4 v4.0.0 github.com/stretchr/testify v1.9.0 - golang.org/x/net v0.27.0 + golang.org/x/net v0.28.0 ) require ( diff --git a/go.sum b/go.sum index e8d86fa..8d41c1b 100644 --- a/go.sum +++ b/go.sum @@ -29,8 +29,8 @@ github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg= github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 2d9be9b7bc6a4daf616b1b74c60590986d5dc534 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Mon, 16 Sep 2024 23:59:57 +0530 Subject: [PATCH 073/114] Add round trip time measurement to candidate pair (#731) * Add round trip time measurement to candidate pair Use the round trip time measurement to populate RTT fields in CandidatePairStats. Atomic and tests * Use int64 nanosecnods to make atomic easier --- agent.go | 6 +++--- agent_stats.go | 6 +++--- agent_test.go | 21 +++++++++++++++++++++ candidatepair.go | 34 ++++++++++++++++++++++++++++++++++ selection.go | 8 ++++++-- 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/agent.go b/agent.go index c7970a5..b9268b8 100644 --- a/agent.go +++ b/agent.go @@ -973,16 +973,16 @@ func (a *Agent) invalidatePendingBindingRequests(filterTime time.Time) { // Assert that the passed TransactionID is in our pendingBindingRequests and returns the destination // If the bindingRequest was valid remove it from our pending cache -func (a *Agent) handleInboundBindingSuccess(id [stun.TransactionIDSize]byte) (bool, *bindingRequest) { +func (a *Agent) handleInboundBindingSuccess(id [stun.TransactionIDSize]byte) (bool, *bindingRequest, time.Duration) { a.invalidatePendingBindingRequests(time.Now()) for i := range a.pendingBindingRequests { if a.pendingBindingRequests[i].transactionID == id { validBindingRequest := a.pendingBindingRequests[i] a.pendingBindingRequests = append(a.pendingBindingRequests[:i], a.pendingBindingRequests[i+1:]...) - return true, &validBindingRequest + return true, &validBindingRequest, time.Since(validBindingRequest.timestamp) } } - return false, nil + return false, nil, 0 } // handleInbound processes STUN traffic from a remote candidate diff --git a/agent_stats.go b/agent_stats.go index 035c652..785e7ff 100644 --- a/agent_stats.go +++ b/agent_stats.go @@ -29,14 +29,14 @@ func (a *Agent) GetCandidatePairsStats() []CandidatePairStats { // FirstRequestTimestamp time.Time // LastRequestTimestamp time.Time // LastResponseTimestamp time.Time - // TotalRoundTripTime float64 - // CurrentRoundTripTime float64 + TotalRoundTripTime: cp.TotalRoundTripTime(), + CurrentRoundTripTime: cp.CurrentRoundTripTime(), // AvailableOutgoingBitrate float64 // AvailableIncomingBitrate float64 // CircuitBreakerTriggerCount uint32 // RequestsReceived uint64 // RequestsSent uint64 - // ResponsesReceived uint64 + ResponsesReceived: cp.ResponsesReceived(), // ResponsesSent uint64 // RetransmissionsReceived uint64 // RetransmissionsSent uint64 diff --git a/agent_test.go b/agent_test.go index b53c7d0..5622ec1 100644 --- a/agent_test.go +++ b/agent_test.go @@ -721,6 +721,10 @@ func TestCandidatePairStats(t *testing.T) { p := a.findPair(hostLocal, prflxRemote) p.state = CandidatePairStateFailed + for i := 0; i < 10; i++ { + p.UpdateRoundTripTime(time.Duration(i+1) * time.Second) + } + stats := a.GetCandidatePairsStats() if len(stats) != 4 { t.Fatal("expected 4 candidate pairs stats") @@ -766,6 +770,23 @@ func TestCandidatePairStats(t *testing.T) { t.Fatalf("expected host-prflx pair to have state failed, it has state %s instead", prflxPairStat.State.String()) } + + expectedCurrentRoundTripTime := time.Duration(10) * time.Second + if prflxPairStat.CurrentRoundTripTime != expectedCurrentRoundTripTime.Seconds() { + t.Fatalf("expected current round trip time to be %f, it is %f instead", + expectedCurrentRoundTripTime.Seconds(), prflxPairStat.CurrentRoundTripTime) + } + + expectedTotalRoundTripTime := time.Duration(55) * time.Second + if prflxPairStat.TotalRoundTripTime != expectedTotalRoundTripTime.Seconds() { + t.Fatalf("expected total round trip time to be %f, it is %f instead", + expectedTotalRoundTripTime.Seconds(), prflxPairStat.TotalRoundTripTime) + } + + if prflxPairStat.ResponsesReceived != 10 { + t.Fatalf("expected responses received to be 10, it is %d instead", + prflxPairStat.ResponsesReceived) + } } func TestLocalCandidateStats(t *testing.T) { diff --git a/candidatepair.go b/candidatepair.go index 2139209..2b27eb1 100644 --- a/candidatepair.go +++ b/candidatepair.go @@ -5,6 +5,8 @@ package ice import ( "fmt" + "sync/atomic" + "time" "github.com/pion/stun/v3" ) @@ -28,6 +30,11 @@ type CandidatePair struct { state CandidatePairState nominated bool nominateOnBindingSuccess bool + + // stats + currentRoundTripTime int64 // in ns + totalRoundTripTime int64 // in ns + responsesReceived uint64 } func (p *CandidatePair) String() string { @@ -100,3 +107,30 @@ func (a *Agent) sendSTUN(msg *stun.Message, local, remote Candidate) { a.log.Tracef("Failed to send STUN message: %s", err) } } + +// UpdateRoundTripTime sets the current round time of this pair and +// accumulates total round trip time and responses received +func (p *CandidatePair) UpdateRoundTripTime(rtt time.Duration) { + rttNs := rtt.Nanoseconds() + atomic.StoreInt64(&p.currentRoundTripTime, rttNs) + atomic.AddInt64(&p.totalRoundTripTime, rttNs) + atomic.AddUint64(&p.responsesReceived, 1) +} + +// CurrentRoundTripTime returns the current round trip time in seconds +// https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime +func (p *CandidatePair) CurrentRoundTripTime() float64 { + return time.Duration(atomic.LoadInt64(&p.currentRoundTripTime)).Seconds() +} + +// TotalRoundTripTime returns the current round trip time in seconds +// https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-totalroundtriptime +func (p *CandidatePair) TotalRoundTripTime() float64 { + return time.Duration(atomic.LoadInt64(&p.totalRoundTripTime)).Seconds() +} + +// ResponsesReceived returns the total number of connectivity responses received +// https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-responsesreceived +func (p *CandidatePair) ResponsesReceived() uint64 { + return atomic.LoadUint64(&p.responsesReceived) +} diff --git a/selection.go b/selection.go index f386d51..d310530 100644 --- a/selection.go +++ b/selection.go @@ -120,7 +120,7 @@ func (s *controllingSelector) HandleBindingRequest(m *stun.Message, local, remot } func (s *controllingSelector) HandleSuccessResponse(m *stun.Message, local, remote Candidate, remoteAddr net.Addr) { - ok, pendingRequest := s.agent.handleInboundBindingSuccess(m.TransactionID) + ok, pendingRequest, rtt := s.agent.handleInboundBindingSuccess(m.TransactionID) if !ok { s.log.Warnf("Discard message from (%s), unknown TransactionID 0x%x", remote, m.TransactionID) return @@ -149,6 +149,8 @@ func (s *controllingSelector) HandleSuccessResponse(m *stun.Message, local, remo if pendingRequest.isUseCandidate && s.agent.getSelectedPair() == nil { s.agent.setSelectedPair(p) } + + p.UpdateRoundTripTime(rtt) } func (s *controllingSelector) PingCandidate(local, remote Candidate) { @@ -211,7 +213,7 @@ func (s *controlledSelector) HandleSuccessResponse(m *stun.Message, local, remot // request with an appropriate error code response (e.g., 400) // [RFC5389]. - ok, pendingRequest := s.agent.handleInboundBindingSuccess(m.TransactionID) + ok, pendingRequest, rtt := s.agent.handleInboundBindingSuccess(m.TransactionID) if !ok { s.log.Warnf("Discard message from (%s), unknown TransactionID 0x%x", remote, m.TransactionID) return @@ -245,6 +247,8 @@ func (s *controlledSelector) HandleSuccessResponse(m *stun.Message, local, remot s.log.Tracef("Ignore nominate new pair %s, already nominated pair %s", p, selectedPair) } } + + p.UpdateRoundTripTime(rtt) } func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote Candidate) { From 1f9684cee4cba59b7db6bca7e67924443ab8b019 Mon Sep 17 00:00:00 2001 From: Daniel Kessler Date: Mon, 23 Sep 2024 17:47:55 +0100 Subject: [PATCH 074/114] Switch udp_mux_test to use sha256 instead of sha1 (#733) Minor change to this test to stop using sha1 and remove the linter exceptions. Co-authored-by: Daniel Kessler --- udp_mux_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/udp_mux_test.go b/udp_mux_test.go index d2c14d7..fa24398 100644 --- a/udp_mux_test.go +++ b/udp_mux_test.go @@ -8,7 +8,7 @@ package ice import ( "crypto/rand" - "crypto/sha1" //nolint:gosec + "crypto/sha256" "encoding/binary" "net" "sync" @@ -216,12 +216,12 @@ func testMuxConnectionPair(t *testing.T, pktConn net.PacketConn, remoteConn *net for written := 0; written < targetSize; { buf := make([]byte, receiveMTU) // Byte 0-4: sequence - // Bytes 4-24: sha1 checksum - // Bytes2 4-mtu: random data - _, err := rand.Read(buf[24:]) + // Bytes 4-36: sha256 checksum + // Bytes2 36-mtu: random data + _, err := rand.Read(buf[36:]) require.NoError(t, err) - h := sha1.Sum(buf[24:]) //nolint:gosec - copy(buf[4:24], h[:]) + h := sha256.Sum256(buf[36:]) + copy(buf[4:36], h[:]) binary.LittleEndian.PutUint32(buf[0:4], uint32(sequence)) _, err = remoteConn.Write(buf) @@ -240,8 +240,8 @@ func testMuxConnectionPair(t *testing.T, pktConn net.PacketConn, remoteConn *net func verifyPacket(t *testing.T, b []byte, nextSeq uint32) { readSeq := binary.LittleEndian.Uint32(b[0:4]) require.Equal(t, nextSeq, readSeq) - h := sha1.Sum(b[24:]) //nolint:gosec - require.Equal(t, h[:], b[4:24]) + h := sha256.Sum256(b[36:]) + require.Equal(t, h[:], b[4:36]) } func TestUDPMux_Agent_Restart(t *testing.T) { From 0a8def8816ba27e88fd42bc52465f09a55fd3714 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 00:40:26 +0000 Subject: [PATCH 075/114] Update module golang.org/x/net to v0.29.0 Generated by renovateBot --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 8bd675f..d0e590e 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pion/transport/v3 v3.0.7 github.com/pion/turn/v4 v4.0.0 github.com/stretchr/testify v1.9.0 - golang.org/x/net v0.28.0 + golang.org/x/net v0.29.0 ) require ( @@ -20,8 +20,8 @@ require ( github.com/kr/pretty v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/wlynxg/anet v0.0.3 // indirect - golang.org/x/crypto v0.26.0 // indirect - golang.org/x/sys v0.24.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/sys v0.25.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 8d41c1b..4c024da 100644 --- a/go.sum +++ b/go.sum @@ -27,12 +27,12 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg= github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 410d6ec36f50b3ee586854f204d59e419e8e1af6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 6 Oct 2024 07:17:56 +0000 Subject: [PATCH 076/114] Update module github.com/pion/dtls/v3 to v3.0.3 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d0e590e..a779c0d 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/google/uuid v1.6.0 - github.com/pion/dtls/v3 v3.0.2 + github.com/pion/dtls/v3 v3.0.3 github.com/pion/logging v0.2.2 github.com/pion/mdns/v2 v2.0.7 github.com/pion/randutil v0.1.0 diff --git a/go.sum b/go.sum index 4c024da..48f0e2d 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pion/dtls/v3 v3.0.2 h1:425DEeJ/jfuTTghhUDW0GtYZYIwwMtnKKJNMcWccTX0= -github.com/pion/dtls/v3 v3.0.2/go.mod h1:dfIXcFkKoujDQ+jtd8M6RgqKK3DuaUilm3YatAbGp5k= +github.com/pion/dtls/v3 v3.0.3 h1:j5ajZbQwff7Z8k3pE3S+rQ4STvKvXUdKsi/07ka+OWM= +github.com/pion/dtls/v3 v3.0.3/go.mod h1:weOTUyIV4z0bQaVzKe8kpaP17+us3yAuiQsEAG1STMU= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= From 854fdfdb5199e4861336d64cdd4e28ade05de5b3 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Mon, 7 Oct 2024 12:38:39 +0530 Subject: [PATCH 077/114] Add ability to get selected candidate pair stats (#735) It is useful to have stats from just the selected pair as a lightweight option where a lot of agents are running, for example, an SFU. lint Switch udp_mux_test to use sha256 instead of sha1 (#733) Minor change to this test to stop using sha1 and remove the linter exceptions. Co-authored-by: Daniel Kessler Update module golang.org/x/net to v0.29.0 Generated by renovateBot Update module github.com/pion/dtls/v3 to v3.0.3 Generated by renovateBot --- agent_stats.go | 50 +++++++++++++++++++++++++++++ agent_test.go | 85 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 134 insertions(+), 1 deletion(-) diff --git a/agent_stats.go b/agent_stats.go index 785e7ff..b18c138 100644 --- a/agent_stats.go +++ b/agent_stats.go @@ -54,6 +54,56 @@ func (a *Agent) GetCandidatePairsStats() []CandidatePairStats { return res } +// GetSelectedCandidatePairStats returns a candidate pair stats for selected candidate pair. +// Returns false if there is no selected pair +func (a *Agent) GetSelectedCandidatePairStats() (CandidatePairStats, bool) { + isAvailable := false + var res CandidatePairStats + err := a.loop.Run(a.loop, func(_ context.Context) { + sp := a.getSelectedPair() + if sp == nil { + return + } + + isAvailable = true + res = CandidatePairStats{ + Timestamp: time.Now(), + LocalCandidateID: sp.Local.ID(), + RemoteCandidateID: sp.Remote.ID(), + State: sp.state, + Nominated: sp.nominated, + // PacketsSent uint32 + // PacketsReceived uint32 + // BytesSent uint64 + // BytesReceived uint64 + // LastPacketSentTimestamp time.Time + // LastPacketReceivedTimestamp time.Time + // FirstRequestTimestamp time.Time + // LastRequestTimestamp time.Time + // LastResponseTimestamp time.Time + TotalRoundTripTime: sp.TotalRoundTripTime(), + CurrentRoundTripTime: sp.CurrentRoundTripTime(), + // AvailableOutgoingBitrate float64 + // AvailableIncomingBitrate float64 + // CircuitBreakerTriggerCount uint32 + // RequestsReceived uint64 + // RequestsSent uint64 + ResponsesReceived: sp.ResponsesReceived(), + // ResponsesSent uint64 + // RetransmissionsReceived uint64 + // RetransmissionsSent uint64 + // ConsentRequestsSent uint64 + // ConsentExpiredTimestamp time.Time + } + }) + if err != nil { + a.log.Errorf("Failed to get selected candidate pair stats: %v", err) + return CandidatePairStats{}, false + } + + return res, isAvailable +} + // GetLocalCandidatesStats returns a list of local candidates stats func (a *Agent) GetLocalCandidatesStats() []CandidateStats { var res []CandidateStats diff --git a/agent_test.go b/agent_test.go index 5622ec1..168fcdd 100644 --- a/agent_test.go +++ b/agent_test.go @@ -635,7 +635,7 @@ func TestInvalidGather(t *testing.T) { }) } -func TestCandidatePairStats(t *testing.T) { +func TestCandidatePairsStats(t *testing.T) { defer test.CheckRoutines(t)() // Avoid deadlocks? @@ -789,6 +789,89 @@ func TestCandidatePairStats(t *testing.T) { } } +func TestSelectedCandidatePairStats(t *testing.T) { + defer test.CheckRoutines(t)() + + // Avoid deadlocks? + defer test.TimeOut(1 * time.Second).Stop() + + a, err := NewAgent(&AgentConfig{}) + if err != nil { + t.Fatalf("Failed to create agent: %s", err) + } + defer func() { + require.NoError(t, a.Close()) + }() + + hostConfig := &CandidateHostConfig{ + Network: "udp", + Address: "192.168.1.1", + Port: 19216, + Component: 1, + } + hostLocal, err := NewCandidateHost(hostConfig) + if err != nil { + t.Fatalf("Failed to construct local host candidate: %s", err) + } + + srflxConfig := &CandidateServerReflexiveConfig{ + Network: "udp", + Address: "10.10.10.2", + Port: 19218, + Component: 1, + RelAddr: "4.3.2.1", + RelPort: 43212, + } + srflxRemote, err := NewCandidateServerReflexive(srflxConfig) + if err != nil { + t.Fatalf("Failed to construct remote srflx candidate: %s", err) + } + + // no selected pair, should return not available + _, ok := a.GetSelectedCandidatePairStats() + require.False(t, ok) + + // add pair and populate some RTT stats + p := a.findPair(hostLocal, srflxRemote) + if p == nil { + a.addPair(hostLocal, srflxRemote) + p = a.findPair(hostLocal, srflxRemote) + } + for i := 0; i < 10; i++ { + p.UpdateRoundTripTime(time.Duration(i+1) * time.Second) + } + + // set the pair as selected + a.setSelectedPair(p) + + stats, ok := a.GetSelectedCandidatePairStats() + require.True(t, ok) + + if stats.LocalCandidateID != hostLocal.ID() { + t.Fatal("invalid local candidate id") + } + if stats.RemoteCandidateID != srflxRemote.ID() { + t.Fatal("invalid remote candidate id") + } + + expectedCurrentRoundTripTime := time.Duration(10) * time.Second + if stats.CurrentRoundTripTime != expectedCurrentRoundTripTime.Seconds() { + t.Fatalf("expected current round trip time to be %f, it is %f instead", + expectedCurrentRoundTripTime.Seconds(), stats.CurrentRoundTripTime) + } + + expectedTotalRoundTripTime := time.Duration(55) * time.Second + if stats.TotalRoundTripTime != expectedTotalRoundTripTime.Seconds() { + t.Fatalf("expected total round trip time to be %f, it is %f instead", + expectedTotalRoundTripTime.Seconds(), stats.TotalRoundTripTime) + } + + if stats.ResponsesReceived != 10 { + t.Fatalf("expected responses received to be 10, it is %d instead", + stats.ResponsesReceived) + } +} + func TestLocalCandidateStats(t *testing.T) { defer test.CheckRoutines(t)() From 166b1b7f9e257d03d39bd0df16ffb772fd003796 Mon Sep 17 00:00:00 2001 From: ARJUN SHAJI Date: Mon, 28 Oct 2024 18:11:21 +0530 Subject: [PATCH 078/114] Use sync.Pool for candidate inbound buffer This commit reduces garbage collection pressure by re-using the buffer used for reading inbound ICE traffic. Fixes #737 --- candidate_base.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/candidate_base.go b/candidate_base.go index 7d06368..ab4d373 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -12,6 +12,7 @@ import ( "net" "strconv" "strings" + "sync" "sync/atomic" "time" @@ -212,6 +213,12 @@ func (c *candidateBase) start(a *Agent, conn net.PacketConn, initializedCh <-cha go c.recvLoop(initializedCh) } +var bufferPool = sync.Pool{ // nolint:gochecknoglobals + New: func() interface{} { + return make([]byte, receiveMTU) + }, +} + func (c *candidateBase) recvLoop(initializedCh <-chan struct{}) { a := c.agent() @@ -223,7 +230,13 @@ func (c *candidateBase) recvLoop(initializedCh <-chan struct{}) { return } - buf := make([]byte, receiveMTU) + bufferPoolBuffer := bufferPool.Get() + defer bufferPool.Put(bufferPoolBuffer) + buf, ok := bufferPoolBuffer.([]byte) + if !ok { + return + } + for { n, srcAddr, err := c.conn.ReadFrom(buf) if err != nil { From 9407bb0d2ac6684b9c35a4a73667aea3f47a8097 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Thu, 31 Oct 2024 11:05:14 +0530 Subject: [PATCH 079/114] Accept use-candidate unconditionally for ice-lite (#739) There could be a mismatch between the two ends in candidate priority when using peer reflexive. It happens in the following scenario 1. Client has two srflx candidates. a. The first one gets discovered by LiveKit server as prflx. b. The second one gets added via ice-trickle first and then gets a STUN ping. So, it is srflx remote candidate from server's point-of-view. 2. This leads to a priority issue. a. Both candidates have same priority from client's point-of-view (both are srflx). b. But, from server's point-of-view, the first candidate has higher priority (prflx). 3. The first candidate establishes connectivity and becomes the selected pair (client is ICE controlling and server is ICE controlled, server is in ICE lite). 4. libwebrtc does a sort and switch some time later based on RTT. As client side has both at same priority, RTT based sorting could make the second candidate the preferred one. So, the client sends useCandidate=1 for the second candidate. pion/ice does not switch because the selected pair is at higher priority due to prflx candidate. 5. STUN pings do not happen and the ICE connection eventually fails. ICE controlled agent should accept use-candidate unconditionally if it is an ICE lite agentt. Just in case existing behaviour is needed, it can be configured using `EnableUseCandidateCheckPriority`. NOTE: With aggressive nomination, the selected pair could change a few times, but should eventually settle on what the controlling side wants. --- agent.go | 8 ++ agent_config.go | 7 ++ agent_test.go | 205 ++++++++++++++++++++++++++++++------------------ selection.go | 4 +- 4 files changed, 144 insertions(+), 80 deletions(-) diff --git a/agent.go b/agent.go index b9268b8..0364de7 100644 --- a/agent.go +++ b/agent.go @@ -145,6 +145,8 @@ type Agent struct { insecureSkipVerify bool proxyDialer proxy.Dialer + + enableUseCandidateCheckPriority bool } // NewAgent creates a new Agent @@ -219,6 +221,8 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit disableActiveTCP: config.DisableActiveTCP, userBindingRequestHandler: config.BindingRequestHandler, + + enableUseCandidateCheckPriority: config.EnableUseCandidateCheckPriority, } a.connectionStateNotifier = &handlerNotifier{connectionStateFunc: a.onConnectionStateChange, done: make(chan struct{})} a.candidateNotifier = &handlerNotifier{candidateFunc: a.onCandidate, done: make(chan struct{})} @@ -1219,3 +1223,7 @@ func (a *Agent) setGatheringState(newState GatheringState) error { <-done return nil } + +func (a *Agent) needsToCheckPriorityOnNominated() bool { + return !a.lite || a.enableUseCandidateCheckPriority +} diff --git a/agent_config.go b/agent_config.go index 93e8889..ad3dd49 100644 --- a/agent_config.go +++ b/agent_config.go @@ -200,6 +200,13 @@ type AgentConfig struct { // * Implement draft-thatcher-ice-renomination // * Implement custom CandidatePair switching logic BindingRequestHandler func(m *stun.Message, local, remote Candidate, pair *CandidatePair) bool + + // EnableUseCandidateCheckPriority can be used to enable checking for equal or higher priority to + // switch selected candidate pair if the peer requests USE-CANDIDATE and agent is a lite agent. + // This is disabled by default, i. e. when peer requests USE-CANDIDATE, the selected pair will be + // switched to that irrespective of relative priority between current selected pair + // and priority of the pair being switched to. + EnableUseCandidateCheckPriority bool } // initWithDefaults populates an agent and falls back to defaults if fields are unset diff --git a/agent_test.go b/agent_test.go index 168fcdd..4b64976 100644 --- a/agent_test.go +++ b/agent_test.go @@ -1844,99 +1844,148 @@ func TestAcceptAggressiveNomination(t *testing.T) { require.NoError(t, wan.Start()) - aNotifier, aConnected := onConnected() - bNotifier, bConnected := onConnected() - - KeepaliveInterval := time.Hour - cfg0 := &AgentConfig{ - NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}, - MulticastDNSMode: MulticastDNSModeDisabled, - Net: net0, - - KeepaliveInterval: &KeepaliveInterval, - CheckInterval: &KeepaliveInterval, - AcceptAggressiveNomination: true, + testCases := []struct { + name string + isLite bool + enableUseCandidateCheckPriority bool + useHigherPriority bool + isExpectedToSwitch bool + }{ + {"should accept higher priority - full agent", false, false, true, true}, + {"should not accept lower priority - full agent", false, false, false, false}, + {"should accept higher priority - no use-candidate priority check - lite agent", true, false, true, true}, + {"should accept lower priority - no use-candidate priority check - lite agent", true, false, false, true}, + {"should accept higher priority - use-candidate priority check - lite agent", true, true, true, true}, + {"should not accept lower priority - use-candidate priority check - lite agent", true, true, false, false}, } - var aAgent, bAgent *Agent - aAgent, err = NewAgent(cfg0) - require.NoError(t, err) - defer func() { - require.NoError(t, aAgent.Close()) - }() - require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + aNotifier, aConnected := onConnected() + bNotifier, bConnected := onConnected() - cfg1 := &AgentConfig{ - NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}, - MulticastDNSMode: MulticastDNSModeDisabled, - Net: net1, - KeepaliveInterval: &KeepaliveInterval, - CheckInterval: &KeepaliveInterval, - } + KeepaliveInterval := time.Hour + cfg0 := &AgentConfig{ + NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}, + MulticastDNSMode: MulticastDNSModeDisabled, + Net: net0, + KeepaliveInterval: &KeepaliveInterval, + CheckInterval: &KeepaliveInterval, + Lite: tc.isLite, + EnableUseCandidateCheckPriority: tc.enableUseCandidateCheckPriority, + } + if tc.isLite { + cfg0.CandidateTypes = []CandidateType{CandidateTypeHost} + } - bAgent, err = NewAgent(cfg1) - require.NoError(t, err) - defer func() { - require.NoError(t, bAgent.Close()) - }() - require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) + var aAgent, bAgent *Agent + aAgent, err = NewAgent(cfg0) + require.NoError(t, err) + defer func() { + require.NoError(t, aAgent.Close()) + }() + require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) - connect(aAgent, bAgent) + cfg1 := &AgentConfig{ + NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}, + MulticastDNSMode: MulticastDNSModeDisabled, + Net: net1, + KeepaliveInterval: &KeepaliveInterval, + CheckInterval: &KeepaliveInterval, + } - // Ensure pair selected - // Note: this assumes ConnectionStateConnected is thrown after selecting the final pair - <-aConnected - <-bConnected + bAgent, err = NewAgent(cfg1) + require.NoError(t, err) + defer func() { + require.NoError(t, bAgent.Close()) + }() + require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) - // Send new USE-CANDIDATE message with higher priority to update the selected pair - buildMsg := func(class stun.MessageClass, username, key string, priority uint32) *stun.Message { - msg, err1 := stun.Build(stun.NewType(stun.MethodBinding, class), stun.TransactionID, - stun.NewUsername(username), - stun.NewShortTermIntegrity(key), - UseCandidate(), - PriorityAttr(priority), - stun.Fingerprint, - ) - require.NoError(t, err1) + connect(aAgent, bAgent) - return msg - } + // Ensure pair selected + // Note: this assumes ConnectionStateConnected is thrown after selecting the final pair + <-aConnected + <-bConnected - selectedCh := make(chan Candidate, 1) - var expectNewSelectedCandidate Candidate - err = aAgent.OnSelectedCandidatePairChange(func(_, remote Candidate) { - selectedCh <- remote - }) - require.NoError(t, err) - var bcandidates []Candidate - bcandidates, err = bAgent.GetLocalCandidates() - require.NoError(t, err) + // Send new USE-CANDIDATE message with priority to update the selected pair + buildMsg := func(class stun.MessageClass, username, key string, priority uint32) *stun.Message { + msg, err1 := stun.Build(stun.NewType(stun.MethodBinding, class), stun.TransactionID, + stun.NewUsername(username), + stun.NewShortTermIntegrity(key), + UseCandidate(), + PriorityAttr(priority), + stun.Fingerprint, + ) + require.NoError(t, err1) - for _, c := range bcandidates { - if c != bAgent.getSelectedPair().Local { - if expectNewSelectedCandidate == nil { - incr_priority: - for _, candidates := range aAgent.remoteCandidates { - for _, candidate := range candidates { - if candidate.Equal(c) { - candidate.(*CandidateHost).priorityOverride += 1000 //nolint:forcetypeassert - break incr_priority + return msg + } + + selectedCh := make(chan Candidate, 1) + var expectNewSelectedCandidate Candidate + err = aAgent.OnSelectedCandidatePairChange(func(_, remote Candidate) { + selectedCh <- remote + }) + require.NoError(t, err) + var bcandidates []Candidate + bcandidates, err = bAgent.GetLocalCandidates() + require.NoError(t, err) + + for _, c := range bcandidates { + if c != bAgent.getSelectedPair().Local { + if expectNewSelectedCandidate == nil { + expected_change_priority: + for _, candidates := range aAgent.remoteCandidates { + for _, candidate := range candidates { + if candidate.Equal(c) { + if tc.useHigherPriority { + candidate.(*CandidateHost).priorityOverride += 1000 //nolint:forcetypeassert + } else { + candidate.(*CandidateHost).priorityOverride -= 1000 //nolint:forcetypeassert + } + break expected_change_priority + } + } + } + if tc.isExpectedToSwitch { + expectNewSelectedCandidate = c + } else { + expectNewSelectedCandidate = aAgent.getSelectedPair().Remote + } + } else { + // a smaller change for other candidates other the new expected one + change_priority: + for _, candidates := range aAgent.remoteCandidates { + for _, candidate := range candidates { + if candidate.Equal(c) { + if tc.useHigherPriority { + candidate.(*CandidateHost).priorityOverride += 500 //nolint:forcetypeassert + } else { + candidate.(*CandidateHost).priorityOverride -= 500 //nolint:forcetypeassert + } + break change_priority + } + } } } + _, err = c.writeTo(buildMsg(stun.ClassRequest, aAgent.localUfrag+":"+aAgent.remoteUfrag, aAgent.localPwd, c.Priority()).Raw, bAgent.getSelectedPair().Remote) + require.NoError(t, err) } - expectNewSelectedCandidate = c } - _, err = c.writeTo(buildMsg(stun.ClassRequest, aAgent.localUfrag+":"+aAgent.remoteUfrag, aAgent.localPwd, c.Priority()).Raw, bAgent.getSelectedPair().Remote) - require.NoError(t, err) - } - } - time.Sleep(1 * time.Second) - select { - case selected := <-selectedCh: - require.True(t, selected.Equal(expectNewSelectedCandidate)) - default: - t.Fatal("No selected candidate pair") + time.Sleep(1 * time.Second) + select { + case selected := <-selectedCh: + require.True(t, selected.Equal(expectNewSelectedCandidate)) + default: + if !tc.isExpectedToSwitch { + require.True(t, aAgent.getSelectedPair().Remote.Equal(expectNewSelectedCandidate)) + } else { + t.Fatal("No selected candidate pair") + } + } + }) } require.NoError(t, wan.Stop()) diff --git a/selection.go b/selection.go index d310530..9aa4cad 100644 --- a/selection.go +++ b/selection.go @@ -241,7 +241,7 @@ func (s *controlledSelector) HandleSuccessResponse(m *stun.Message, local, remot s.log.Tracef("Found valid candidate pair: %s", p) if p.nominateOnBindingSuccess { if selectedPair := s.agent.getSelectedPair(); selectedPair == nil || - (selectedPair != p && selectedPair.priority() <= p.priority()) { + (selectedPair != p && (!s.agent.needsToCheckPriorityOnNominated() || selectedPair.priority() <= p.priority())) { s.agent.setSelectedPair(p) } else if selectedPair != p { s.log.Tracef("Ignore nominate new pair %s, already nominated pair %s", p, selectedPair) @@ -266,7 +266,7 @@ func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote // generated a valid pair (Section 7.2.5.3.2). The agent sets the // nominated flag value of the valid pair to true. selectedPair := s.agent.getSelectedPair() - if selectedPair == nil || (selectedPair != p && selectedPair.priority() <= p.priority()) { + if selectedPair == nil || (selectedPair != p && (!s.agent.needsToCheckPriorityOnNominated() || selectedPair.priority() <= p.priority())) { s.agent.setSelectedPair(p) } else if selectedPair != p { s.log.Tracef("Ignore nominate new pair %s, already nominated pair %s", p, selectedPair) From 59d8563508553c4f6a6ed51c897e4f87eb0d2ac0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 22:13:41 +0000 Subject: [PATCH 080/114] Update module github.com/pion/dtls/v3 to v3.0.4 Generated by renovateBot --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index a779c0d..8c48b83 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/google/uuid v1.6.0 - github.com/pion/dtls/v3 v3.0.3 + github.com/pion/dtls/v3 v3.0.4 github.com/pion/logging v0.2.2 github.com/pion/mdns/v2 v2.0.7 github.com/pion/randutil v0.1.0 @@ -12,7 +12,7 @@ require ( github.com/pion/transport/v3 v3.0.7 github.com/pion/turn/v4 v4.0.0 github.com/stretchr/testify v1.9.0 - golang.org/x/net v0.29.0 + golang.org/x/net v0.30.0 ) require ( @@ -20,8 +20,8 @@ require ( github.com/kr/pretty v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/wlynxg/anet v0.0.3 // indirect - golang.org/x/crypto v0.27.0 // indirect - golang.org/x/sys v0.25.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/sys v0.26.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 48f0e2d..8a7d81f 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pion/dtls/v3 v3.0.3 h1:j5ajZbQwff7Z8k3pE3S+rQ4STvKvXUdKsi/07ka+OWM= -github.com/pion/dtls/v3 v3.0.3/go.mod h1:weOTUyIV4z0bQaVzKe8kpaP17+us3yAuiQsEAG1STMU= +github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U= +github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= @@ -27,12 +27,12 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg= github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 2c699d8405d70c9f5a8268132ef735f3609add5d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 23 Nov 2024 17:09:40 +0000 Subject: [PATCH 081/114] Update module github.com/stretchr/testify to v1.10.0 (#743) Generated by renovateBot Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8c48b83..e35280d 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/pion/stun/v3 v3.0.0 github.com/pion/transport/v3 v3.0.7 github.com/pion/turn/v4 v4.0.0 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 golang.org/x/net v0.30.0 ) diff --git a/go.sum b/go.sum index 8a7d81f..0a2d3fd 100644 --- a/go.sum +++ b/go.sum @@ -23,8 +23,8 @@ github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM= github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg= github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= From 1c850ea1d8a1292ca5867f2b49907848ca27ce8a Mon Sep 17 00:00:00 2001 From: Nikita Karmatskikh Date: Tue, 26 Nov 2024 13:12:05 +0300 Subject: [PATCH 082/114] Pass UDPAddr.Zone to net.ListenUDP listenUDPInPortRange wasn't passing the Zone and would always fail to bind. This would cause high CPU usage as every port in the range would be attempted and fail. Fixes #742 --- net.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net.go b/net.go index 6e740a7..c03bbee 100644 --- a/net.go +++ b/net.go @@ -137,8 +137,13 @@ func listenUDPInPortRange(n transport.Net, log logging.LeveledLogger, portMax, p portStart := globalMathRandomGenerator.Intn(j-i+1) + i portCurrent := portStart for { - lAddr = &net.UDPAddr{IP: lAddr.IP, Port: portCurrent} - c, e := n.ListenUDP(network, lAddr) + addr := &net.UDPAddr{ + IP: lAddr.IP, + Zone: lAddr.Zone, + Port: portCurrent, + } + + c, e := n.ListenUDP(network, addr) if e == nil { return c, e //nolint:nilerr } From 8b8fffdc3cec1627ea4013dcd57b8879d22f5e91 Mon Sep 17 00:00:00 2001 From: WofWca Date: Tue, 26 Nov 2024 16:08:14 +0400 Subject: [PATCH 083/114] Use named return val for IP/if filter This should make it clear that you need to return `true` to keep it and `false` to exclude. Relates to pion/webrtc#2958 --- agent.go | 4 ++-- agent_config.go | 4 ++-- gather_vnet_test.go | 6 +++--- net.go | 4 ++-- net_test.go | 2 +- udp_mux_multi.go | 8 ++++---- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/agent.go b/agent.go index 0364de7..1346771 100644 --- a/agent.go +++ b/agent.go @@ -138,8 +138,8 @@ type Agent struct { udpMux UDPMux udpMuxSrflx UniversalUDPMux - interfaceFilter func(string) bool - ipFilter func(net.IP) bool + interfaceFilter func(string) (keep bool) + ipFilter func(net.IP) (keep bool) includeLoopback bool insecureSkipVerify bool diff --git a/agent_config.go b/agent_config.go index ad3dd49..8d3f281 100644 --- a/agent_config.go +++ b/agent_config.go @@ -148,11 +148,11 @@ type AgentConfig struct { // InterfaceFilter is a function that you can use in order to whitelist or blacklist // the interfaces which are used to gather ICE candidates. - InterfaceFilter func(string) bool + InterfaceFilter func(string) (keep bool) // IPFilter is a function that you can use in order to whitelist or blacklist // the ips which are used to gather ICE candidates. - IPFilter func(net.IP) bool + IPFilter func(net.IP) (keep bool) // InsecureSkipVerify controls if self-signed certificates are accepted when connecting // to TURN servers via TLS or DTLS diff --git a/gather_vnet_test.go b/gather_vnet_test.go index 3babce3..f8754d3 100644 --- a/gather_vnet_test.go +++ b/gather_vnet_test.go @@ -387,7 +387,7 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { t.Run("InterfaceFilter should exclude the interface", func(t *testing.T) { a, err := NewAgent(&AgentConfig{ Net: nw, - InterfaceFilter: func(interfaceName string) bool { + InterfaceFilter: func(interfaceName string) (keep bool) { require.Equal(t, "eth0", interfaceName) return false }, @@ -408,7 +408,7 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { t.Run("IPFilter should exclude the IP", func(t *testing.T) { a, err := NewAgent(&AgentConfig{ Net: nw, - IPFilter: func(ip net.IP) bool { + IPFilter: func(ip net.IP) (keep bool) { require.Equal(t, net.IP{1, 2, 3, 1}, ip) return false }, @@ -429,7 +429,7 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { t.Run("InterfaceFilter should not exclude the interface", func(t *testing.T) { a, err := NewAgent(&AgentConfig{ Net: nw, - InterfaceFilter: func(interfaceName string) bool { + InterfaceFilter: func(interfaceName string) (keep bool) { require.Equal(t, "eth0", interfaceName) return true }, diff --git a/net.go b/net.go index c03bbee..e60e646 100644 --- a/net.go +++ b/net.go @@ -39,8 +39,8 @@ func isZeros(ip net.IP) bool { //nolint:gocognit func localInterfaces( n transport.Net, - interfaceFilter func(string) bool, - ipFilter func(net.IP) bool, + interfaceFilter func(string) (keep bool), + ipFilter func(net.IP) (keep bool), networkTypes []NetworkType, includeLoopback bool, ) ([]*transport.Interface, []netip.Addr, error) { diff --git a/net_test.go b/net_test.go index 12f0a9e..cd7d2fb 100644 --- a/net_test.go +++ b/net_test.go @@ -45,7 +45,7 @@ func TestCreateAddr(t *testing.T) { require.Equal(t, &net.TCPAddr{IP: ipv6.AsSlice(), Port: port}, createAddr(NetworkTypeTCP6, ipv6, port)) } -func problematicNetworkInterfaces(s string) bool { +func problematicNetworkInterfaces(s string) (keep bool) { defaultDockerBridgeNetwork := strings.Contains(s, "docker") customDockerBridgeNetwork := strings.Contains(s, "br-") diff --git a/udp_mux_multi.go b/udp_mux_multi.go index 2594cb0..f8b4285 100644 --- a/udp_mux_multi.go +++ b/udp_mux_multi.go @@ -141,8 +141,8 @@ type UDPMuxFromPortOption interface { } type multiUDPMuxFromPortParam struct { - ifFilter func(string) bool - ipFilter func(ip net.IP) bool + ifFilter func(string) (keep bool) + ipFilter func(ip net.IP) (keep bool) networks []NetworkType readBufferSize int writeBufferSize int @@ -160,7 +160,7 @@ func (o *udpMuxFromPortOption) apply(p *multiUDPMuxFromPortParam) { } // UDPMuxFromPortWithInterfaceFilter set the filter to filter out interfaces that should not be used -func UDPMuxFromPortWithInterfaceFilter(f func(string) bool) UDPMuxFromPortOption { +func UDPMuxFromPortWithInterfaceFilter(f func(string) (keep bool)) UDPMuxFromPortOption { return &udpMuxFromPortOption{ f: func(p *multiUDPMuxFromPortParam) { p.ifFilter = f @@ -169,7 +169,7 @@ func UDPMuxFromPortWithInterfaceFilter(f func(string) bool) UDPMuxFromPortOption } // UDPMuxFromPortWithIPFilter set the filter to filter out IP addresses that should not be used -func UDPMuxFromPortWithIPFilter(f func(ip net.IP) bool) UDPMuxFromPortOption { +func UDPMuxFromPortWithIPFilter(f func(ip net.IP) (keep bool)) UDPMuxFromPortOption { return &udpMuxFromPortOption{ f: func(p *multiUDPMuxFromPortParam) { p.ipFilter = f From 35bb3fb992f8f70c4613364583e4d84864e98565 Mon Sep 17 00:00:00 2001 From: Amin Cheloh Date: Mon, 2 Sep 2024 22:06:38 +0700 Subject: [PATCH 084/114] docs(agent_config.go): update KeepaliveInterval default value to 2 sec Update documentation for KeepaliveInterval to match defaultKeepaliveInterval constant --- agent_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent_config.go b/agent_config.go index 8d3f281..7c7ecf6 100644 --- a/agent_config.go +++ b/agent_config.go @@ -91,7 +91,7 @@ type AgentConfig struct { // KeepaliveInterval determines how often should we send ICE // keepalives (should be less then connectiontimeout above) - // when this is nil, it defaults to 10 seconds. + // when this is nil, it defaults to 2 seconds. // A keepalive interval of 0 means we never send keepalive packets KeepaliveInterval *time.Duration From c9abe8bfe02352889c03ed1e74adf9e1a6fdbc86 Mon Sep 17 00:00:00 2001 From: Daniel Kessler Date: Thu, 9 Jan 2025 15:48:42 -0800 Subject: [PATCH 085/114] Add nil checks to agent_handlers (#751) Co-authored-by: Daniel Kessler --- agent_handlers.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agent_handlers.go b/agent_handlers.go index 0c02277..c245f0c 100644 --- a/agent_handlers.go +++ b/agent_handlers.go @@ -26,19 +26,19 @@ func (a *Agent) OnCandidate(f func(Candidate)) error { } func (a *Agent) onSelectedCandidatePairChange(p *CandidatePair) { - if h, ok := a.onSelectedCandidatePairChangeHdlr.Load().(func(Candidate, Candidate)); ok { + if h, ok := a.onSelectedCandidatePairChangeHdlr.Load().(func(Candidate, Candidate)); ok && h != nil { h(p.Local, p.Remote) } } func (a *Agent) onCandidate(c Candidate) { - if onCandidateHdlr, ok := a.onCandidateHdlr.Load().(func(Candidate)); ok { + if onCandidateHdlr, ok := a.onCandidateHdlr.Load().(func(Candidate)); ok && onCandidateHdlr != nil { onCandidateHdlr(c) } } func (a *Agent) onConnectionStateChange(s ConnectionState) { - if hdlr, ok := a.onConnectionStateChangeHdlr.Load().(func(ConnectionState)); ok { + if hdlr, ok := a.onConnectionStateChangeHdlr.Load().(func(ConnectionState)); ok && hdlr != nil { hdlr(s) } } From abdc0cadecf914182e772b27ff74223891073d53 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Mon, 13 Jan 2025 04:48:00 -0800 Subject: [PATCH 086/114] Use addrEqual for candidate comparison (#752) --- candidate_base.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/candidate_base.go b/candidate_base.go index ab4d373..ac9d857 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -402,7 +402,7 @@ func (c *candidateBase) Equal(other Candidate) bool { if c.addr() == nil || other.addr() == nil { return false } - if c.addr().String() != other.addr().String() { + if !addrEqual(c.addr(), other.addr()) { return false } } From ab6e243686cb841d927aac41dac1c096746db708 Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Tue, 14 Jan 2025 12:00:59 -0600 Subject: [PATCH 087/114] Parse Candidate Extensions (RFC5245) - Rewrote `UnmarshalCandidate` to better align with RFC5245. - Added Candidate `Extensions` and `GetExtension`. - Updated `Equal` and `Marshal` to accommodate these changes. - New Type `CandidateExtension` to handle. --- candidate.go | 16 + candidate_base.go | 535 +++++++++++++++++++++++++++----- candidate_test.go | 768 +++++++++++++++++++++++++++++++++++++++++++++- errors.go | 2 + 4 files changed, 1242 insertions(+), 79 deletions(-) diff --git a/candidate.go b/candidate.go index 4324159..9fb2b05 100644 --- a/candidate.go +++ b/candidate.go @@ -52,12 +52,28 @@ type Candidate interface { // candidate, which is useful for diagnostics and other purposes RelatedAddress() *CandidateRelatedAddress + // Extensions returns a copy of all extension attributes associated with the ICECandidate. + // In the order of insertion, *(key value). + // Extension attributes are defined in RFC 5245, Section 15.1: + // https://datatracker.ietf.org/doc/html/rfc5245#section-15.1 + //. + Extensions() []CandidateExtension + + // GetExtension returns the value of the extension attribute associated with the ICECandidate. + // Extension attributes are defined in RFC 5245, Section 15.1: + // https://datatracker.ietf.org/doc/html/rfc5245#section-15.1 + //. + GetExtension(key string) (value CandidateExtension, ok bool) + String() string Type() CandidateType TCPType() TCPType Equal(other Candidate) bool + // DeepEqual same as Equal, But it also compares the candidate extensions. + DeepEqual(other Candidate) bool + Marshal() string addr() net.Addr diff --git a/candidate_base.go b/candidate_base.go index ac9d857..c165648 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -45,6 +45,7 @@ type candidateBase struct { remoteCandidateCaches map[AddrPort]Candidate isLocationTracked bool + extensions []CandidateExtension } // Done implements context.Context @@ -406,6 +407,7 @@ func (c *candidateBase) Equal(other Candidate) bool { return false } } + return c.NetworkType() == other.NetworkType() && c.Type() == other.Type() && c.Address() == other.Address() && @@ -414,6 +416,11 @@ func (c *candidateBase) Equal(other Candidate) bool { c.RelatedAddress().Equal(other.RelatedAddress()) } +// DeepEqual is same as Equal but also compares the extensions +func (c *candidateBase) DeepEqual(other Candidate) bool { + return c.Equal(other) && c.extensionsEqual(other.Extensions()) +} + // String makes the candidateBase printable func (c *candidateBase) String() string { return fmt.Sprintf("%s %s %s%s (resolved: %v)", c.NetworkType(), c.Type(), net.JoinHostPort(c.Address(), strconv.Itoa(c.Port())), c.relatedAddress, c.resolvedAddr) @@ -496,10 +503,6 @@ func (c *candidateBase) Marshal() string { c.Port(), c.Type()) - if c.tcpType != TCPTypeUnspecified { - val += fmt.Sprintf(" tcptype %s", c.tcpType.String()) - } - if r := c.RelatedAddress(); r != nil && r.Address != "" && r.Port != 0 { val = fmt.Sprintf("%s raddr %s rport %d", val, @@ -507,92 +510,468 @@ func (c *candidateBase) Marshal() string { r.Port) } + extensions := c.marshalExtensions() + + if extensions != "" { + val = fmt.Sprintf("%s %s", val, extensions) + } + return val } -// UnmarshalCandidate creates a Candidate from its string representation -func UnmarshalCandidate(raw string) (Candidate, error) { - split := strings.Fields(raw) - // Foundation not specified: not RFC 8445 compliant but seen in the wild - if len(raw) != 0 && raw[0] == ' ' { - split = append([]string{" "}, split...) - } - if len(split) < 8 { - return nil, fmt.Errorf("%w (%d)", errAttributeTooShortICECandidate, len(split)) +// CandidateExtension represents a single candidate extension +// as defined in https://tools.ietf.org/html/rfc5245#section-15.1 +// . +type CandidateExtension struct { + Key string + Value string +} + +func (c *candidateBase) Extensions() []CandidateExtension { + // IF Extensions were not parsed using UnmarshalCandidate + // For backwards compatibility when the TCPType is set manually + if len(c.extensions) == 0 && c.TCPType() != TCPTypeUnspecified { + return []CandidateExtension{{ + Key: "tcptype", + Value: c.TCPType().String(), + }} } - // Foundation - foundation := split[0] + extensions := make([]CandidateExtension, len(c.extensions)) + copy(extensions, c.extensions) - // Component - rawComponent, err := strconv.ParseUint(split[1], 10, 16) - if err != nil { - return nil, fmt.Errorf("%w: %v", errParseComponent, err) //nolint:errorlint - } - component := uint16(rawComponent) + return extensions +} - // Protocol - protocol := split[2] +// Get returns the value of the given key if it exists. +func (c *candidateBase) GetExtension(key string) (CandidateExtension, bool) { + extension := CandidateExtension{Key: key} - // Priority - priorityRaw, err := strconv.ParseUint(split[3], 10, 32) - if err != nil { - return nil, fmt.Errorf("%w: %v", errParsePriority, err) //nolint:errorlint - } - priority := uint32(priorityRaw) + for i := range c.extensions { + if c.extensions[i].Key == key { + extension.Value = c.extensions[i].Value - // Address - address := removeZoneIDFromAddress(split[4]) - - // Port - rawPort, err := strconv.ParseUint(split[5], 10, 16) - if err != nil { - return nil, fmt.Errorf("%w: %v", errParsePort, err) //nolint:errorlint - } - port := int(rawPort) - typ := split[7] - - relatedAddress := "" - relatedPort := 0 - tcpType := TCPTypeUnspecified - - if len(split) > 8 { - split = split[8:] - - if split[0] == "raddr" { - if len(split) < 4 { - return nil, fmt.Errorf("%w: incorrect length", errParseRelatedAddr) - } - - // RelatedAddress - relatedAddress = split[1] - - // RelatedPort - rawRelatedPort, parseErr := strconv.ParseUint(split[3], 10, 16) - if parseErr != nil { - return nil, fmt.Errorf("%w: %v", errParsePort, parseErr) //nolint:errorlint - } - relatedPort = int(rawRelatedPort) - } else if split[0] == "tcptype" { - if len(split) < 2 { - return nil, fmt.Errorf("%w: incorrect length", errParseTCPType) - } - - tcpType = NewTCPType(split[1]) + return extension, true } } - switch typ { - case "host": - return NewCandidateHost(&CandidateHostConfig{"", protocol, address, port, component, priority, foundation, tcpType, false}) - case "srflx": - return NewCandidateServerReflexive(&CandidateServerReflexiveConfig{"", protocol, address, port, component, priority, foundation, relatedAddress, relatedPort}) - case "prflx": - return NewCandidatePeerReflexive(&CandidatePeerReflexiveConfig{"", protocol, address, port, component, priority, foundation, relatedAddress, relatedPort}) - case "relay": - return NewCandidateRelay(&CandidateRelayConfig{"", protocol, address, port, component, priority, foundation, relatedAddress, relatedPort, "", nil}) - default: + // TCPType was manually set. + if key == "tcptype" && c.TCPType() != TCPTypeUnspecified { + extension.Value = c.TCPType().String() + + return extension, true } - return nil, fmt.Errorf("%w (%s)", ErrUnknownCandidateTyp, typ) + return extension, false +} + +// marshalExtensions returns the string representation of the candidate extensions. +func (c *candidateBase) marshalExtensions() string { + value := "" + exts := c.Extensions() + + for i := range exts { + if value != "" { + value += " " + } + + value += exts[i].Key + " " + exts[i].Value + } + + return value +} + +// Equal returns true if the candidate extensions are equal. +func (c *candidateBase) extensionsEqual(other []CandidateExtension) bool { + freq1 := make(map[CandidateExtension]int) + freq2 := make(map[CandidateExtension]int) + + if len(c.extensions) != len(other) { + return false + } + + if len(c.extensions) == 0 { + return true + } + + if len(c.extensions) == 1 { + return c.extensions[0] == other[0] + } + + for i := range c.extensions { + freq1[c.extensions[i]]++ + freq2[other[i]]++ + } + + for k, v := range freq1 { + if freq2[k] != v { + return false + } + } + + return true +} + +func (c *candidateBase) setExtensions(extensions []CandidateExtension) { + c.extensions = extensions +} + +// UnmarshalCandidate Parses a candidate from a string +// https://datatracker.ietf.org/doc/html/rfc5245#section-15.1 +func UnmarshalCandidate(raw string) (Candidate, error) { + // rfc5245 + + pos := 0 + + // foundation ( 1*32ice-char ) But we allow for empty foundation, + foundation, pos, err := readCandidateCharToken(raw, pos, 32) + if err != nil { + return nil, fmt.Errorf("%w: %v in %s", errParseFoundation, err, raw) //nolint:errorlint // we wrap the error + } + + // Empty foundation, not RFC 8445 compliant but seen in the wild + if foundation == "" { + foundation = " " + } + + if pos >= len(raw) { + return nil, fmt.Errorf("%w: expected component in %s", errAttributeTooShortICECandidate, raw) + } + + // component-id ( 1*5DIGIT ) + component, pos, err := readCandidateDigitToken(raw, pos, 5) + if err != nil { + return nil, fmt.Errorf("%w: %v in %s", errParseComponent, err, raw) //nolint:errorlint // we wrap the error + } + + if pos >= len(raw) { + return nil, fmt.Errorf("%w: expected transport in %s", errAttributeTooShortICECandidate, raw) + } + + // transport ( "UDP" / transport-extension ; from RFC 3261 ) SP + protocol, pos := readCandidateStringToken(raw, pos) + + if pos >= len(raw) { + return nil, fmt.Errorf("%w: expected priority in %s", errAttributeTooShortICECandidate, raw) + } + + // priority ( 1*10DIGIT ) SP + priority, pos, err := readCandidateDigitToken(raw, pos, 10) + if err != nil { + return nil, fmt.Errorf("%w: %v in %s", errParsePriority, err, raw) //nolint:errorlint // we wrap the error + } + + if pos >= len(raw) { + return nil, fmt.Errorf("%w: expected address in %s", errAttributeTooShortICECandidate, raw) + } + + // connection-address SP ;from RFC 4566 + address, pos := readCandidateStringToken(raw, pos) + + // Remove IPv6 ZoneID: https://github.com/pion/ice/pull/704 + address = removeZoneIDFromAddress(address) + + if pos >= len(raw) { + return nil, fmt.Errorf("%w: expected port in %s", errAttributeTooShortICECandidate, raw) + } + + // port from RFC 4566 + port, pos, err := readCandidatePort(raw, pos) + if err != nil { + return nil, fmt.Errorf("%w: %v in %s", errParsePort, err, raw) //nolint:errorlint // we wrap the error + } + + // "typ" SP + typeKey, pos := readCandidateStringToken(raw, pos) + if typeKey != "typ" { + return nil, fmt.Errorf("%w (%s)", ErrUnknownCandidateTyp, typeKey) + } + + if pos >= len(raw) { + return nil, fmt.Errorf("%w: expected candidate type in %s", errAttributeTooShortICECandidate, raw) + } + + // SP cand-type ("host" / "srflx" / "prflx" / "relay") + typ, pos := readCandidateStringToken(raw, pos) + + raddr, rport, pos, err := tryReadRelativeAddrs(raw, pos) + if err != nil { + return nil, err + } + + tcpType := TCPTypeUnspecified + var extensions []CandidateExtension + var tcpTypeRaw string + + if pos < len(raw) { + extensions, tcpTypeRaw, err = unmarshalCandidateExtensions(raw[pos:]) + if err != nil { + return nil, fmt.Errorf("%w: %v", errParseExtension, err) //nolint:errorlint // we wrap the error + } + + if tcpTypeRaw != "" { + tcpType = NewTCPType(tcpTypeRaw) + if tcpType == TCPTypeUnspecified { + return nil, fmt.Errorf("%w: invalid or unsupported TCPtype %s", errParseTCPType, tcpTypeRaw) + } + } + } + + // this code is ugly because we can't break backwards compatibility + // with the old way of parsing candidates + switch typ { + case "host": + candidate, err := NewCandidateHost(&CandidateHostConfig{ + "", + protocol, + address, + port, + uint16(component), //nolint:gosec // G115 no overflow we read 5 digits + uint32(priority), //nolint:gosec // G115 no overflow we read 5 digits + foundation, + tcpType, + false, + }) + if err != nil { + return nil, err + } + + candidate.setExtensions(extensions) + + return candidate, nil + case "srflx": + candidate, err := NewCandidateServerReflexive(&CandidateServerReflexiveConfig{ + "", + protocol, + address, + port, + uint16(component), //nolint:gosec // G115 no overflow we read 5 digits + uint32(priority), //nolint:gosec // G115 no overflow we read 5 digits + foundation, + raddr, + rport, + }) + if err != nil { + return nil, err + } + + candidate.setExtensions(extensions) + + return candidate, nil + case "prflx": + candidate, err := NewCandidatePeerReflexive(&CandidatePeerReflexiveConfig{ + "", + protocol, + address, + port, + uint16(component), //nolint:gosec // G115 no overflow we read 5 digits + uint32(priority), //nolint:gosec // G115 no overflow we read 5 digits + foundation, + raddr, + rport, + }) + if err != nil { + return nil, err + } + + candidate.setExtensions(extensions) + + return candidate, nil + case "relay": + candidate, err := NewCandidateRelay(&CandidateRelayConfig{ + "", + protocol, + address, + port, + uint16(component), //nolint:gosec // G115 no overflow we read 5 digits + uint32(priority), //nolint:gosec // G115 no overflow we read 5 digits + foundation, + raddr, + rport, + "", + nil, + }) + if err != nil { + return nil, err + } + + candidate.setExtensions(extensions) + + return candidate, nil + default: + return nil, fmt.Errorf("%w (%s)", ErrUnknownCandidateTyp, typ) + } +} + +// Read an ice-char token from the raw string +// ice-char = ALPHA / DIGIT / "+" / "/" +// stop reading when a space is encountered or the end of the string +func readCandidateCharToken(raw string, start int, limit int) (string, int, error) { + for i, char := range raw[start:] { + if char == 0x20 { // SP + return raw[start : start+i], start + i + 1, nil + } + + if i == limit { + return "", 0, fmt.Errorf("token too long: %s expected 1x%d", raw[start:start+i], limit) //nolint: err113 // handled by caller + } + + if !(char >= 'A' && char <= 'Z' || + char >= 'a' && char <= 'z' || + char >= '0' && char <= '9' || + char == '+' || char == '/') { + return "", 0, fmt.Errorf("invalid ice-char token: %c", char) //nolint: err113 // handled by caller + } + } + + return raw[start:], len(raw), nil +} + +// Read an ice string token from the raw string until a space is encountered +// Or the end of the string, we imply that ice string are UTF-8 encoded +func readCandidateStringToken(raw string, start int) (string, int) { + for i, char := range raw[start:] { + if char == 0x20 { // SP + return raw[start : start+i], start + i + 1 + } + } + + return raw[start:], len(raw) +} + +// Read a digit token from the raw string +// stop reading when a space is encountered or the end of the string +func readCandidateDigitToken(raw string, start, limit int) (int, int, error) { + var val int + for i, char := range raw[start:] { + if char == 0x20 { // SP + return val, start + i + 1, nil + } + + if i == limit { + return 0, 0, fmt.Errorf("token too long: %s expected 1x%d", raw[start:start+i], limit) //nolint: err113 // handled by caller + } + + if !(char >= '0' && char <= '9') { + return 0, 0, fmt.Errorf("invalid digit token: %c", char) //nolint: err113 // handled by caller + } + + val = val*10 + int(char-'0') + } + + return val, len(raw), nil +} + +// Read and validate RFC 4566 port from the raw string +func readCandidatePort(raw string, start int) (int, int, error) { + port, pos, err := readCandidateDigitToken(raw, start, 5) + if err != nil { + return 0, 0, err + } + + if port > 65535 { + return 0, 0, fmt.Errorf("invalid RFC 4566 port %d", port) //nolint: err113 // handled by caller + } + + return port, pos, nil +} + +// Read a byte-string token from the raw string +// As defined in RFC 4566 1*(%x01-09/%x0B-0C/%x0E-FF) ;any byte except NUL, CR, or LF +// we imply that extensions byte-string are UTF-8 encoded +func readCandidateByteString(raw string, start int) (string, int, error) { + for i, char := range raw[start:] { + if char == 0x20 { // SP + return raw[start : start+i], start + i + 1, nil + } + + // 1*(%x01-09/%x0B-0C/%x0E-FF) + if !(char >= 0x01 && char <= 0x09 || + char >= 0x0B && char <= 0x0C || + char >= 0x0E && char <= 0xFF) { + return "", 0, fmt.Errorf("invalid byte-string character: %c", char) //nolint: err113 // handled by caller + } + } + + return raw[start:], len(raw), nil +} + +// Read and validate raddr and rport from the raw string +// [SP rel-addr] [SP rel-port] +// defined in https://datatracker.ietf.org/doc/html/rfc5245#section-15.1 +// . +func tryReadRelativeAddrs(raw string, start int) (raddr string, rport, pos int, err error) { + key, pos := readCandidateStringToken(raw, start) + + if key != "raddr" { + return "", 0, start, nil + } + + if pos >= len(raw) { + return "", 0, 0, fmt.Errorf("%w: expected raddr value in %s", errParseRelatedAddr, raw) + } + + raddr, pos = readCandidateStringToken(raw, pos) + + if pos >= len(raw) { + return "", 0, 0, fmt.Errorf("%w: expected rport in %s", errParseRelatedAddr, raw) + } + + key, pos = readCandidateStringToken(raw, pos) + if key != "rport" { + return "", 0, 0, fmt.Errorf("%w: expected rport in %s", errParseRelatedAddr, raw) + } + + if pos >= len(raw) { + return "", 0, 0, fmt.Errorf("%w: expected rport value in %s", errParseRelatedAddr, raw) + } + + rport, pos, err = readCandidatePort(raw, pos) + if err != nil { + return "", 0, 0, fmt.Errorf("%w: %v", errParseRelatedAddr, err) //nolint:errorlint // we wrap the error + } + + return raddr, rport, pos, nil +} + +// UnmarshalCandidateExtensions parses the candidate extensions from the raw string. +// *(SP extension-att-name SP extension-att-value) +// Where extension-att-name, and extension-att-value are byte-strings +// as defined in https://tools.ietf.org/html/rfc5245#section-15.1 +func unmarshalCandidateExtensions(raw string) (extensions []CandidateExtension, rawTCPTypeRaw string, err error) { + extensions = make([]CandidateExtension, 0) + + if raw == "" { + return extensions, "", nil + } + + if raw[0] == 0x20 { // SP + return extensions, "", fmt.Errorf("%w: unexpected space %s", errParseExtension, raw) + } + + for i := 0; i < len(raw); { + key, next, err := readCandidateByteString(raw, i) + if err != nil { + return extensions, "", fmt.Errorf("%w: failed to read key %v", errParseExtension, err) //nolint: errorlint // we wrap the error + } + i = next + + if i >= len(raw) { + return extensions, "", fmt.Errorf("%w: missing value for %s in %s", errParseExtension, key, raw) + } + + value, next, err := readCandidateByteString(raw, i) + if err != nil { + return extensions, "", fmt.Errorf("%w: failed to read value %v", errParseExtension, err) //nolint: errorlint // we are wrapping the error + } + i = next + + if key == "tcptype" { + rawTCPTypeRaw = value + } + + extensions = append(extensions, CandidateExtension{key, value}) + } + + return } diff --git a/candidate_test.go b/candidate_test.go index aecea11..8a17d0b 100644 --- a/candidate_test.go +++ b/candidate_test.go @@ -274,6 +274,19 @@ func mustCandidateHost(conf *CandidateHostConfig) Candidate { return cand } +func mustCandidateHostWithExtensions(t *testing.T, conf *CandidateHostConfig, extensions []CandidateExtension) Candidate { + t.Helper() + + cand, err := NewCandidateHost(conf) + if err != nil { + panic(err) + } + + cand.setExtensions(extensions) + + return cand +} + func mustCandidateRelay(conf *CandidateRelayConfig) Candidate { cand, err := NewCandidateRelay(conf) if err != nil { @@ -282,6 +295,19 @@ func mustCandidateRelay(conf *CandidateRelayConfig) Candidate { return cand } +func mustCandidateRelayWithExtensions(t *testing.T, conf *CandidateRelayConfig, extensions []CandidateExtension) Candidate { + t.Helper() + + cand, err := NewCandidateRelay(conf) + if err != nil { + panic(err) + } + + cand.setExtensions(extensions) + + return cand +} + func mustCandidateServerReflexive(conf *CandidateServerReflexiveConfig) Candidate { cand, err := NewCandidateServerReflexive(conf) if err != nil { @@ -290,6 +316,32 @@ func mustCandidateServerReflexive(conf *CandidateServerReflexiveConfig) Candidat return cand } +func mustCandidateServerReflexiveWithExtensions(t *testing.T, conf *CandidateServerReflexiveConfig, extensions []CandidateExtension) Candidate { + t.Helper() + + cand, err := NewCandidateServerReflexive(conf) + if err != nil { + panic(err) + } + + cand.setExtensions(extensions) + + return cand +} + +func mustCandidatePeerReflexiveWithExtensions(t *testing.T, conf *CandidatePeerReflexiveConfig, extensions []CandidateExtension) Candidate { + t.Helper() + + cand, err := NewCandidatePeerReflexive(conf) + if err != nil { + panic(err) + } + + cand.setExtensions(extensions) + + return cand +} + func TestCandidateMarshal(t *testing.T) { for idx, test := range []struct { candidate Candidate @@ -327,6 +379,25 @@ func TestCandidateMarshal(t *testing.T) { "647372371 1 udp 1694498815 191.228.238.68 53991 typ srflx raddr 192.168.0.274 rport 53991", false, }, + { + mustCandidatePeerReflexiveWithExtensions( + t, + &CandidatePeerReflexiveConfig{ + Network: NetworkTypeTCP4.String(), + Address: "192.0.2.15", + Port: 50000, + RelAddr: "10.0.0.1", + RelPort: 12345, + }, + []CandidateExtension{ + {"generation", "0"}, + {"network-id", "2"}, + {"network-cost", "10"}, + }, + ), + "4207374052 1 tcp 1685790463 192.0.2.15 50000 typ prflx raddr 10.0.0.1 rport 12345 generation 0 network-id 2 network-cost 10", + false, + }, { mustCandidateRelay(&CandidateRelayConfig{ Network: NetworkTypeUDP4.String(), @@ -368,6 +439,28 @@ func TestCandidateMarshal(t *testing.T) { " 1 udp 500 " + localhostIPStr + " 80 typ host", false, }, + { + mustCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: localhostIPStr, + Port: 80, + Priority: 500, + Foundation: "+/3713fhi", + }), + "+/3713fhi 1 udp 500 " + localhostIPStr + " 80 typ host", + false, + }, + { + mustCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeTCP4.String(), + Address: "172.28.142.173", + Port: 7686, + Priority: 1671430143, + Foundation: "+/3713fhi", + }), + "3359356140 1 tcp 1671430143 172.28.142.173 7686 typ host", + false, + }, // Invalid candidates {nil, "", true}, @@ -382,11 +475,52 @@ func TestCandidateMarshal(t *testing.T) { {nil, "4207374051 INVALID udp 2130706431 10.0.75.1 INVALID typ host", true}, {nil, "4207374051 1 udp 2130706431 10.0.75.1 53634 typ INVALID", true}, {nil, "4207374051 1 INVALID 2130706431 10.0.75.1 53634 typ host", true}, + {nil, "4207374051 1 INVALID 2130706431 10.0.75.1 53634 typ", true}, + {nil, "4207374051 1 INVALID 2130706431 10.0.75.1 53634", true}, + {nil, "848194626 1 udp 16777215 50.0.0.^^1 5000 typ relay raddr 192.168.0.1 rport 5001", true}, + {nil, "4207374052 1 tcp 1685790463 192.0#.2.15 50000 typ prflx raddr 10.0.0.1 rport 12345 rport 5001", true}, + {nil, "647372371 1 udp 1694498815 191.228.2@338.68 53991 typ srflx raddr 192.168.0.274 rport 53991", true}, + // invalid foundion; longer than 32 characters + {nil, "111111111111111111111111111111111 1 udp 500 " + localhostIPStr + " 80 typ host", true}, + // Invalid ice-char + {nil, "3$3 1 udp 500 " + localhostIPStr + " 80 typ host", true}, + // invalid component; longer than 5 digits + {nil, "4207374051 123456 udp 500 " + localhostIPStr + " 0 typ host", true}, + // invalid priority; longer than 10 digits + {nil, "4207374051 99999 udp 12345678910 " + localhostIPStr + " 99999 typ host", true}, + // invalid port; + {nil, "4207374051 99999 udp 500 " + localhostIPStr + " 65536 typ host", true}, + {nil, "4207374051 99999 udp 500 " + localhostIPStr + " 999999 typ host", true}, + {nil, "848194626 1 udp 16777215 50.0.0.1 5000 typ relay raddr 192.168.0.1 rport 999999", true}, + + // bad byte-string in extension value + {nil, "750 1 udp 500 fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a 53987 typ host ext valu\nu", true}, + {nil, "848194626 1 udp 16777215 50.0.0.1 5000 typ relay raddr 192.168.0.1 rport 654 ext valu\nu", true}, + {nil, "848194626 1 udp 16777215 50.0.0.1 5000 typ relay raddr 192.168.0.1 rport 654 ext valu\000e", true}, + + // bad byte-string in extension key + {nil, "848194626 1 udp 16777215 50.0.0.1 5000 typ relay raddr 192.168.0.1 rport 654 ext\r value", true}, + + // invalid tcptype + {nil, "1052353102 1 tcp 2128609279 192.168.0.196 0 typ host tcptype INVALID", true}, + + // expect rport after raddr + {nil, "848194626 1 udp 16777215 50.0.0.1 5000 typ relay raddr 192.168.0.1 extension 322", true}, + {nil, "848194626 1 udp 16777215 50.0.0.1 5000 typ relay raddr 192.168.0.1 rport", true}, + {nil, "848194626 1 udp 16777215 50.0.0.1 5000 typ relay raddr 192.168.0.1", true}, + {nil, "848194626 1 udp 16777215 50.0.0.1 5000 typ relay raddr", true}, + {nil, "4207374051 99999 udp 500 " + localhostIPStr + " 80 typ", true}, + {nil, "4207374051 99999 udp 500 " + localhostIPStr + " 80", true}, + {nil, "4207374051 99999 udp 500 " + localhostIPStr, true}, + {nil, "4207374051 99999 udp 500 ", true}, + {nil, "4207374051 99999 udp", true}, + {nil, "4207374051 99999", true}, + {nil, "4207374051", true}, } { t.Run(strconv.Itoa(idx), func(t *testing.T) { actualCandidate, err := UnmarshalCandidate(test.marshaled) if test.expectError { - require.Error(t, err) + require.Error(t, err, "expected error", test.marshaled) return } @@ -466,3 +600,635 @@ func TestMarshalUnmarshalCandidateWithZoneID(t *testing.T) { require.NoError(t, err) require.Truef(t, candidate.Equal(candidate2), "%s != %s", candidate.String(), candidate2.String()) } + +func TestCandidateExtensionsMarshal(t *testing.T) { + testCases := []struct { + Extensions []CandidateExtension + candidate string + }{ + { + []CandidateExtension{ + {"generation", "0"}, + {"ufrag", "QNvE"}, + {"network-id", "4"}, + }, + "1299692247 1 udp 2122134271 fdc8:cc8:c835:e400:343c:feb:32c8:17b9 58240 typ host generation 0 ufrag QNvE network-id 4", + }, + { + []CandidateExtension{ + {"generation", "1"}, + {"network-id", "2"}, + {"network-cost", "50"}, + }, + "647372371 1 udp 1694498815 191.228.238.68 53991 typ srflx raddr 192.168.0.274 rport 53991 generation 1 network-id 2 network-cost 50", + }, + { + []CandidateExtension{ + {"generation", "0"}, + {"network-id", "2"}, + {"network-cost", "10"}, + }, + "4207374052 1 tcp 1685790463 192.0.2.15 50000 typ prflx raddr 10.0.0.1 rport 12345 generation 0 network-id 2 network-cost 10", + }, + { + []CandidateExtension{ + {"generation", "0"}, + {"network-id", "1"}, + {"network-cost", "20"}, + {"ufrag", "frag42abcdef"}, + {"password", "abc123exp123"}, + }, + "848194626 1 udp 16777215 50.0.0.1 5000 typ relay raddr 192.168.0.1 rport 5001 generation 0 network-id 1 network-cost 20 ufrag frag42abcdef password abc123exp123", + }, + { + []CandidateExtension{ + {"tcptype", "active"}, + {"generation", "0"}, + }, + "1052353102 1 tcp 2128609279 192.168.0.196 0 typ host tcptype active generation 0", + }, + { + []CandidateExtension{ + {"tcptype", "active"}, + {"generation", "0"}, + }, + "1052353102 1 tcp 2128609279 192.168.0.196 0 typ host tcptype active generation 0", + }, + { + []CandidateExtension{}, + "1052353102 1 tcp 2128609279 192.168.0.196 0 typ host", + }, + { + []CandidateExtension{}, + "1052353102 1 tcp 2128609279 192.168.0.196 0 typ host", + }, + } + + for _, tc := range testCases { + candidate, err := UnmarshalCandidate(tc.candidate) + require.NoError(t, err) + require.Equal(t, tc.Extensions, candidate.Extensions(), "Extensions should be equal", tc.candidate) + + valueStr := candidate.Marshal() + candidate2, err := UnmarshalCandidate(valueStr) + + require.NoError(t, err) + require.Equal(t, tc.Extensions, candidate2.Extensions(), "Marshal() should preserve extensions") + } +} + +func TestCandidateExtensionsDeepEqual(t *testing.T) { + noExt, err := UnmarshalCandidate("750 0 udp 500 fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a 53987 typ host") + require.NoError(t, err) + + generation := "0" + ufrag := "QNvE" + networkID := "4" + + extensions := []CandidateExtension{ + {"generation", generation}, + {"ufrag", ufrag}, + {"network-id", networkID}, + } + + candidate, err := UnmarshalCandidate( + "750 0 udp 500 fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a 53987 typ host generation " + + generation + " ufrag " + ufrag + " network-id " + networkID, + ) + require.NoError(t, err) + + testCases := []struct { + a Candidate + b Candidate + equal bool + }{ + { + mustCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }), + noExt, + true, + }, + { + mustCandidateHostWithExtensions( + t, + &CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }, + []CandidateExtension{}, + ), + noExt, + true, + }, + { + mustCandidateHostWithExtensions( + t, + &CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }, + extensions, + ), + candidate, + true, + }, + { + mustCandidateRelayWithExtensions( + t, + &CandidateRelayConfig{ + Network: NetworkTypeUDP4.String(), + Address: "10.0.0.10", + Port: 5000, + RelAddr: "10.0.0.2", + RelPort: 5001, + }, + []CandidateExtension{ + {"generation", "0"}, + {"network-id", "1"}, + }, + ), + mustCandidateRelayWithExtensions( + t, + &CandidateRelayConfig{ + Network: NetworkTypeUDP4.String(), + Address: "10.0.0.10", + Port: 5000, + RelAddr: "10.0.0.2", + RelPort: 5001, + }, + []CandidateExtension{ + {"network-id", "1"}, + {"generation", "0"}, + }, + ), + true, + }, + { + mustCandidatePeerReflexiveWithExtensions( + t, + &CandidatePeerReflexiveConfig{ + Network: NetworkTypeTCP4.String(), + Address: "192.0.2.15", + Port: 50000, + RelAddr: "10.0.0.1", + RelPort: 12345, + }, + []CandidateExtension{ + {"generation", "0"}, + {"network-id", "2"}, + {"network-cost", "10"}, + }, + ), + mustCandidatePeerReflexiveWithExtensions( + t, + &CandidatePeerReflexiveConfig{ + Network: NetworkTypeTCP4.String(), + Address: "192.0.2.15", + Port: 50000, + RelAddr: "10.0.0.1", + RelPort: 12345, + }, + []CandidateExtension{ + {"generation", "0"}, + {"network-id", "2"}, + {"network-cost", "10"}, + }, + ), + true, + }, + { + mustCandidateServerReflexiveWithExtensions( + t, + &CandidateServerReflexiveConfig{ + Network: NetworkTypeUDP4.String(), + Address: "191.228.238.68", + Port: 53991, + RelAddr: "192.168.0.274", + RelPort: 53991, + }, + []CandidateExtension{ + {"generation", "0"}, + {"network-id", "2"}, + {"network-cost", "10"}, + }, + ), + mustCandidateServerReflexiveWithExtensions( + t, + &CandidateServerReflexiveConfig{ + Network: NetworkTypeUDP4.String(), + Address: "191.228.238.68", + Port: 53991, + RelAddr: "192.168.0.274", + RelPort: 53991, + }, + []CandidateExtension{ + {"generation", "0"}, + {"network-id", "2"}, + {"network-cost", "10"}, + }, + ), + true, + }, + { + mustCandidateHostWithExtensions( + t, + &CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }, + []CandidateExtension{ + {"generation", "5"}, + {"ufrag", ufrag}, + {"network-id", networkID}, + }, + ), + candidate, + false, + }, + { + mustCandidateHostWithExtensions( + t, + &CandidateHostConfig{ + Network: NetworkTypeTCP4.String(), + Address: "192.168.0.196", + Port: 0, + Priority: 2128609279, + Foundation: "1052353102", + TCPType: TCPTypeActive, + }, + []CandidateExtension{ + {"tcptype", TCPTypeActive.String()}, + {"generation", "0"}, + }, + ), + mustCandidateHostWithExtensions( + t, + &CandidateHostConfig{ + Network: NetworkTypeTCP4.String(), + Address: "192.168.0.197", + Port: 0, + Priority: 2128609279, + Foundation: "1052353102", + TCPType: TCPTypeActive, + }, + []CandidateExtension{ + {"tcptype", TCPTypeActive.String()}, + {"generation", "0"}, + }, + ), + false, + }, + } + + for _, tc := range testCases { + require.Equal(t, tc.a.DeepEqual(tc.b), tc.equal, "a: %s, b: %s", tc.a.Marshal(), tc.b.Marshal()) + } +} + +func TestUnmarshalCandidateExtensions(t *testing.T) { + testCases := []struct { + name string + value string + expected []CandidateExtension + fail bool + }{ + { + name: "empty string", + value: "", + expected: []CandidateExtension{}, + fail: false, + }, + { + name: "valid extension string", + value: "a b c d", + expected: []CandidateExtension{{"a", "b"}, {"c", "d"}}, + fail: false, + }, + { + name: "valid extension string", + value: "a b empty c d", + expected: []CandidateExtension{ + {"a", "b"}, + {"empty", ""}, + {"c", "d"}, + }, + fail: false, + }, + { + name: "invalid extension string", + value: "invalid", + expected: []CandidateExtension{}, + fail: true, + }, + { + name: "invalid extension", + value: " a b", + expected: []CandidateExtension{{"a", "b"}, {"c", "d"}}, + fail: true, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + req := require.New(t) + + actual, _, err := unmarshalCandidateExtensions(testCase.value) + if testCase.fail { + req.Error(err) + } else { + req.NoError(err) + req.EqualValuesf( + testCase.expected, + actual, + "UnmarshalCandidateExtensions() did not return the expected value %v", + testCase.value, + ) + } + }) + } +} + +func TestCandidateGetExtension(t *testing.T) { + t.Run("Get extension", func(t *testing.T) { + extensions := []CandidateExtension{ + {"a", "b"}, + {"c", "d"}, + } + candidate, err := NewCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }) + if err != nil { + t.Error(err) + } + + candidate.setExtensions(extensions) + + value, ok := candidate.GetExtension("c") + require.True(t, ok) + require.Equal(t, "c", value.Key) + require.Equal(t, "d", value.Value) + + value, ok = candidate.GetExtension("a") + require.True(t, ok) + require.Equal(t, "a", value.Key) + require.Equal(t, "b", value.Value) + + value, ok = candidate.GetExtension("b") + require.False(t, ok) + require.Equal(t, "b", value.Key) + require.Equal(t, "", value.Value) + }) + + // This is undefined behavior in the spec; extension-att-name is not unique + // but it implied that it's unique in the implementation + t.Run("Extension with multiple values", func(t *testing.T) { + extensions := []CandidateExtension{ + {"a", "1"}, + {"a", "2"}, + } + + candidate, err := NewCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }) + if err != nil { + t.Error(err) + } + + candidate.setExtensions(extensions) + + value, ok := candidate.GetExtension("a") + require.True(t, ok) + require.Equal(t, "a", value.Key) + require.Equal(t, "1", value.Value) + }) + + t.Run("TCPType extension", func(t *testing.T) { + extensions := []CandidateExtension{ + {"tcptype", "passive"}, + } + + candidate, err := NewCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeTCP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + TCPType: TCPTypeActive, + }) + if err != nil { + t.Error(err) + } + + tcpType, ok := candidate.GetExtension("tcptype") + + require.True(t, ok) + require.Equal(t, "tcptype", tcpType.Key) + require.Equal(t, TCPTypeActive.String(), tcpType.Value) + + candidate.setExtensions(extensions) + + tcpType, ok = candidate.GetExtension("tcptype") + + require.True(t, ok) + require.Equal(t, "tcptype", tcpType.Key) + require.Equal(t, "passive", tcpType.Value) + + candidate2, err := NewCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeTCP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }) + if err != nil { + t.Error(err) + } + + tcpType, ok = candidate2.GetExtension("tcptype") + + require.False(t, ok) + require.Equal(t, "tcptype", tcpType.Key) + require.Equal(t, "", tcpType.Value) + }) +} + +func TestBaseCandidateMarshalExtensions(t *testing.T) { + t.Run("Marshal extension", func(t *testing.T) { + extensions := []CandidateExtension{ + {"generation", "0"}, + {"ValuE", "KeE"}, + {"empty", ""}, + {"another", "value"}, + } + candidate, err := NewCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }) + if err != nil { + t.Error(err) + } + + candidate.setExtensions(extensions) + + value := candidate.marshalExtensions() + require.Equal(t, "generation 0 ValuE KeE empty another value", value) + }) + + t.Run("Marshal Empty", func(t *testing.T) { + candidate, err := NewCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }) + if err != nil { + t.Error(err) + } + + value := candidate.marshalExtensions() + require.Equal(t, "", value) + }) + + t.Run("Marshal TCPType no extension", func(t *testing.T) { + candidate, err := NewCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + TCPType: TCPTypeActive, + }) + if err != nil { + t.Error(err) + } + + value := candidate.marshalExtensions() + require.Equal(t, "tcptype active", value) + }) +} + +func TestBaseCandidateExtensionsEqual(t *testing.T) { + testCases := []struct { + name string + extensions1 []CandidateExtension + extensions2 []CandidateExtension + expected bool + }{ + { + name: "Empty extensions", + extensions1: []CandidateExtension{}, + extensions2: []CandidateExtension{}, + expected: true, + }, + { + name: "Single value extensions", + extensions1: []CandidateExtension{{"a", "b"}}, + extensions2: []CandidateExtension{{"a", "b"}}, + expected: true, + }, + { + name: "multiple value extensions", + extensions1: []CandidateExtension{ + {"a", "b"}, + {"c", "d"}, + }, + extensions2: []CandidateExtension{ + {"a", "b"}, + {"c", "d"}, + }, + expected: true, + }, + { + name: "unsorted extensions", + extensions1: []CandidateExtension{ + {"c", "d"}, + {"a", "b"}, + }, + extensions2: []CandidateExtension{ + {"a", "b"}, + {"c", "d"}, + }, + expected: true, + }, + { + name: "different values", + extensions1: []CandidateExtension{ + {"a", "b"}, + {"c", "d"}, + }, + extensions2: []CandidateExtension{ + {"a", "b"}, + {"c", "e"}, + }, + expected: false, + }, + { + name: "different size", + extensions1: []CandidateExtension{ + {"a", "b"}, + {"c", "d"}, + }, + extensions2: []CandidateExtension{ + {"a", "b"}, + }, + expected: false, + }, + { + name: "different keys", + extensions1: []CandidateExtension{ + {"a", "b"}, + {"c", "d"}, + }, + extensions2: []CandidateExtension{ + {"a", "b"}, + {"e", "d"}, + }, + expected: false, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + cand, err := NewCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }) + if err != nil { + t.Error(err) + } + + cand.setExtensions(testCase.extensions1) + + require.Equal(t, testCase.expected, cand.extensionsEqual(testCase.extensions2)) + }) + } +} diff --git a/errors.go b/errors.go index 02736cc..f48be14 100644 --- a/errors.go +++ b/errors.go @@ -125,10 +125,12 @@ var ( errNotImplemented = errors.New("not implemented yet") errNoUDPMuxAvailable = errors.New("no UDP mux is available") errNoXorAddrMapping = errors.New("no address mapping") + errParseFoundation = errors.New("failed to parse foundation") errParseComponent = errors.New("failed to parse component") errParsePort = errors.New("failed to parse port") errParsePriority = errors.New("failed to parse priority") errParseRelatedAddr = errors.New("failed to parse related addresses") + errParseExtension = errors.New("failed to parse extension") errParseTCPType = errors.New("failed to parse TCP type") errRead = errors.New("failed to read") errUDPMuxDisabled = errors.New("UDPMux is not enabled") From 647b9786dd03a0b3805d07a9271f98b14c5ad27e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 16 Jan 2025 03:45:24 +0000 Subject: [PATCH 088/114] Update golang.org/x/net to v0.33.0 [security] Generated by renovateBot --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index e35280d..8f3abd4 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/pion/transport/v3 v3.0.7 github.com/pion/turn/v4 v4.0.0 github.com/stretchr/testify v1.10.0 - golang.org/x/net v0.30.0 + golang.org/x/net v0.33.0 ) require ( @@ -20,8 +20,8 @@ require ( github.com/kr/pretty v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/wlynxg/anet v0.0.3 // indirect - golang.org/x/crypto v0.28.0 // indirect - golang.org/x/sys v0.26.0 // indirect + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/sys v0.28.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 0a2d3fd..dbe6197 100644 --- a/go.sum +++ b/go.sum @@ -27,12 +27,12 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg= github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From cad1676659762c0c27d3495b061713c2d150a371 Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Fri, 17 Jan 2025 08:21:15 -0600 Subject: [PATCH 089/114] Upgrade golangci-lint, more linters Introduces new linters, upgrade golangci-lint to version (v1.63.4) --- .golangci.yml | 47 ++- active_tcp.go | 17 +- active_tcp_test.go | 54 +-- addr.go | 18 +- agent.go | 330 +++++++++++------- agent_config.go | 97 ++--- agent_get_best_valid_candidate_pair_test.go | 2 + agent_handlers.go | 25 +- agent_handlers_test.go | 12 +- ..._on_selected_candidate_pair_change_test.go | 6 + agent_stats.go | 31 +- agent_test.go | 274 ++++++++------- agent_udpmux_test.go | 8 +- candidate.go | 6 +- candidate_base.go | 130 ++++--- candidate_host.go | 14 +- candidate_peer_reflexive.go | 4 +- candidate_relay.go | 7 +- candidate_server_reflexive.go | 4 +- candidate_test.go | 145 +++++--- candidatepair.go | 26 +- candidatepair_state.go | 5 +- candidaterelatedaddress.go | 3 +- candidatetype.go | 9 +- connectivity_vnet_test.go | 43 ++- errors.go | 52 +-- examples/ping-pong/main.go | 4 +- external_ip_mapper.go | 25 +- external_ip_mapper_test.go | 174 ++++----- gather.go | 119 ++++++- gather_test.go | 160 +++++---- gather_vnet_test.go | 95 +++-- ice.go | 30 +- icecontrol.go | 6 + icecontrol_test.go | 42 +-- internal/atomic/atomic.go | 7 +- internal/fakenet/mock_conn.go | 2 +- internal/fakenet/packet_conn.go | 7 +- internal/stun/stun.go | 2 +- internal/taskloop/taskloop.go | 10 +- mdns.go | 28 +- net.go | 41 ++- net_test.go | 2 + networktype.go | 11 +- priority.go | 2 + priority_test.go | 8 +- rand_test.go | 18 +- role.go | 1 + selection.go | 116 +++--- selection_test.go | 4 + stats.go | 2 +- tcp_mux.go | 62 +++- tcp_mux_multi.go | 7 +- tcp_packet_conn.go | 26 +- transport.go | 22 +- transport_test.go | 24 +- transport_vnet_test.go | 6 +- udp_mux.go | 48 +-- udp_mux_multi.go | 28 +- udp_mux_multi_test.go | 2 + udp_mux_test.go | 10 +- udp_mux_universal.go | 52 +-- udp_mux_universal_test.go | 2 + udp_muxed_conn.go | 11 +- url.go | 36 +- usecandidate.go | 2 + utils_test.go | 15 + 67 files changed, 1619 insertions(+), 1019 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index a3235be..88cb4fb 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -25,17 +25,32 @@ linters-settings: - ^os.Exit$ - ^panic$ - ^print(ln)?$ + varnamelen: + max-distance: 12 + min-name-length: 2 + ignore-type-assert-ok: true + ignore-map-index-ok: true + ignore-chan-recv-ok: true + ignore-decls: + - i int + - n int + - w io.Writer + - r io.Reader + - b []byte linters: enable: - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers - bidichk # Checks for dangerous unicode character sequences - bodyclose # checks whether HTTP response body is closed successfully + - containedctx # containedctx is a linter that detects struct contained context.Context field - contextcheck # check the function whether use a non-inherited context + - cyclop # checks function and package cyclomatic complexity - decorder # check declaration order and count of types, constants, variables and functions - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - dupl # Tool for code clone detection - durationcheck # check for two durations multiplied together + - err113 # Golang linter to check the errors handling expressions - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. @@ -46,18 +61,17 @@ linters: - forcetypeassert # finds forced type assertions - gci # Gci control golang package import order and make it always deterministic. - gochecknoglobals # Checks that no globals are present in Go code - - gochecknoinits # Checks that no init functions are present in Go code - gocognit # Computes and checks the cognitive complexity of functions - goconst # Finds repeated strings that could be replaced by a constant - gocritic # The most opinionated Go source code linter + - gocyclo # Computes and checks the cyclomatic complexity of functions + - godot # Check if comments end in a period - godox # Tool for detection of FIXME, TODO and other comment keywords - - err113 # Golang linter to check the errors handling expressions - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. - - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. - goprintffuncname # Checks that printf-like functions are named with `f` at the end - gosec # Inspects source code for security problems - gosimple # Linter for Go source code that specializes in simplifying a code @@ -65,9 +79,15 @@ linters: - grouper # An analyzer to analyze expression groups. - importas # Enforces consistent import aliases - ineffassign # Detects when assignments to existing variables are not used + - lll # Reports long lines + - maintidx # maintidx measures the maintainability index of each function. + - makezero # Finds slice declarations with non-zero initial length - misspell # Finds commonly misspelled English words in comments + - nakedret # Finds naked returns in functions greater than a specified function length + - nestif # Reports deeply nested if statements - nilerr # Finds the code that returns nil even if it checks that the error is not nil. - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. + - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity - noctx # noctx finds sending http request without context.Context - predeclared # find code that shadows one of Go's predeclared identifiers - revive # golint replacement, finds style mistakes @@ -75,28 +95,22 @@ linters: - stylecheck # Stylecheck is a replacement for golint - tagliatelle # Checks the struct tags. - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 - - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code - unconvert # Remove unnecessary type conversions - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types + - varnamelen # checks that the length of a variable's name matches its scope - wastedassign # wastedassign finds wasted assignment statements - whitespace # Tool for detection of leading and trailing whitespace disable: - depguard # Go linter that checks if package imports are in a list of acceptable packages - - containedctx # containedctx is a linter that detects struct contained context.Context field - - cyclop # checks function and package cyclomatic complexity - funlen # Tool for detection of long functions - - gocyclo # Computes and checks the cyclomatic complexity of functions - - godot # Check if comments end in a period - - gomnd # An analyzer to detect magic numbers. + - gochecknoinits # Checks that no init functions are present in Go code + - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. + - interfacebloat # A linter that checks length of interface. - ireturn # Accept Interfaces, Return Concrete Types - - lll # Reports long lines - - maintidx # maintidx measures the maintainability index of each function. - - makezero # Finds slice declarations with non-zero initial length - - nakedret # Finds naked returns in functions greater than a specified function length - - nestif # Reports deeply nested if statements - - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity + - mnd # An analyzer to detect magic numbers - nolintlint # Reports ill-formed or insufficient nolint directives - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - prealloc # Finds slice declarations that could potentially be preallocated @@ -104,8 +118,7 @@ linters: - rowserrcheck # checks whether Err of rows is checked successfully - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. - testpackage # linter that makes you use a separate _test package - - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers - - varnamelen # checks that the length of a variable's name matches its scope + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes - wrapcheck # Checks that errors returned from external packages are wrapped - wsl # Whitespace Linter - Forces you to use empty lines! diff --git a/active_tcp.go b/active_tcp.go index a6f8387..b55e650 100644 --- a/active_tcp.go +++ b/active_tcp.go @@ -21,7 +21,12 @@ type activeTCPConn struct { closed int32 } -func newActiveTCPConn(ctx context.Context, localAddress string, remoteAddress netip.AddrPort, log logging.LeveledLogger) (a *activeTCPConn) { +func newActiveTCPConn( + ctx context.Context, + localAddress string, + remoteAddress netip.AddrPort, + log logging.LeveledLogger, +) (a *activeTCPConn) { a = &activeTCPConn{ readBuffer: packetio.NewBuffer(), writeBuffer: packetio.NewBuffer(), @@ -31,7 +36,8 @@ func newActiveTCPConn(ctx context.Context, localAddress string, remoteAddress ne if err != nil { atomic.StoreInt32(&a.closed, 1) log.Infof("Failed to dial TCP address %s: %v", remoteAddress, err) - return + + return a } a.localAddr.Store(laddr) @@ -46,6 +52,7 @@ func newActiveTCPConn(ctx context.Context, localAddress string, remoteAddress ne conn, err := dialer.DialContext(ctx, "tcp", remoteAddress.String()) if err != nil { log.Infof("Failed to dial TCP address %s: %v", remoteAddress, err) + return } a.remoteAddr.Store(conn.RemoteAddr()) @@ -57,11 +64,13 @@ func newActiveTCPConn(ctx context.Context, localAddress string, remoteAddress ne n, err := readStreamingPacket(conn, buff) if err != nil { log.Infof("Failed to read streaming packet: %s", err) + break } if _, err := a.readBuffer.Write(buff[:n]); err != nil { log.Infof("Failed to write to buffer: %s", err) + break } } @@ -73,11 +82,13 @@ func newActiveTCPConn(ctx context.Context, localAddress string, remoteAddress ne n, err := a.writeBuffer.Read(buff) if err != nil { log.Infof("Failed to read from buffer: %s", err) + break } if _, err = writeStreamingPacket(conn, buff[:n]); err != nil { log.Infof("Failed to write streaming packet: %s", err) + break } } @@ -98,6 +109,7 @@ func (a *activeTCPConn) ReadFrom(buff []byte) (n int, srcAddr net.Addr, err erro n, err = a.readBuffer.Read(buff) // RemoteAddr is assuredly set *after* we can read from the buffer srcAddr = a.RemoteAddr() + return } @@ -113,6 +125,7 @@ func (a *activeTCPConn) Close() error { atomic.StoreInt32(&a.closed, 1) _ = a.readBuffer.Close() _ = a.writeBuffer.Close() + return nil } diff --git a/active_tcp_test.go b/active_tcp_test.go index 1e0cb0f..796292b 100644 --- a/active_tcp_test.go +++ b/active_tcp_test.go @@ -21,19 +21,25 @@ import ( ) func getLocalIPAddress(t *testing.T, networkType NetworkType) netip.Addr { + t.Helper() + net, err := stdnet.NewNet() require.NoError(t, err) _, localAddrs, err := localInterfaces(net, problematicNetworkInterfaces, nil, []NetworkType{networkType}, false) require.NoError(t, err) require.NotEmpty(t, localAddrs) + return localAddrs[0] } func ipv6Available(t *testing.T) bool { + t.Helper() + net, err := stdnet.NewNet() require.NoError(t, err) _, localAddrs, err := localInterfaces(net, problematicNetworkInterfaces, nil, []NetworkType{NetworkTypeTCP6}, false) require.NoError(t, err) + return len(localAddrs) > 0 } @@ -89,14 +95,14 @@ func TestActiveTCP(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - r := require.New(t) + req := require.New(t) listener, err := net.ListenTCP("tcp", &net.TCPAddr{ IP: testCase.listenIPAddress.AsSlice(), Port: listenPort, Zone: testCase.listenIPAddress.Zone(), }) - r.NoError(err) + req.NoError(err) defer func() { _ = listener.Close() }() @@ -113,7 +119,7 @@ func TestActiveTCP(t *testing.T) { _ = tcpMux.Close() }() - r.NotNil(tcpMux.LocalAddr(), "tcpMux.LocalAddr() is nil") + req.NotNil(tcpMux.LocalAddr(), "tcpMux.LocalAddr() is nil") hostAcceptanceMinWait := 100 * time.Millisecond cfg := &AgentConfig{ @@ -128,8 +134,8 @@ func TestActiveTCP(t *testing.T) { cfg.MulticastDNSMode = MulticastDNSModeQueryAndGather } passiveAgent, err := NewAgent(cfg) - r.NoError(err) - r.NotNil(passiveAgent) + req.NoError(err) + req.NotNil(passiveAgent) activeAgent, err := NewAgent(&AgentConfig{ CandidateTypes: []CandidateType{CandidateTypeHost}, @@ -138,44 +144,44 @@ func TestActiveTCP(t *testing.T) { HostAcceptanceMinWait: &hostAcceptanceMinWait, InterfaceFilter: problematicNetworkInterfaces, }) - r.NoError(err) - r.NotNil(activeAgent) + req.NoError(err) + req.NotNil(activeAgent) passiveAgentConn, activeAgenConn := connect(passiveAgent, activeAgent) - r.NotNil(passiveAgentConn) - r.NotNil(activeAgenConn) + req.NotNil(passiveAgentConn) + req.NotNil(activeAgenConn) defer func() { - r.NoError(activeAgenConn.Close()) - r.NoError(passiveAgentConn.Close()) + req.NoError(activeAgenConn.Close()) + req.NoError(passiveAgentConn.Close()) }() pair := passiveAgent.getSelectedPair() - r.NotNil(pair) - r.Equal(testCase.selectedPairNetworkType, pair.Local.NetworkType().NetworkShort()) + req.NotNil(pair) + req.Equal(testCase.selectedPairNetworkType, pair.Local.NetworkType().NetworkShort()) foo := []byte("foo") _, err = passiveAgentConn.Write(foo) - r.NoError(err) + req.NoError(err) buffer := make([]byte, 1024) n, err := activeAgenConn.Read(buffer) - r.NoError(err) - r.Equal(foo, buffer[:n]) + req.NoError(err) + req.Equal(foo, buffer[:n]) bar := []byte("bar") _, err = activeAgenConn.Write(bar) - r.NoError(err) + req.NoError(err) n, err = passiveAgentConn.Read(buffer) - r.NoError(err) - r.Equal(bar, buffer[:n]) + req.NoError(err) + req.Equal(bar, buffer[:n]) }) } } -// Assert that Active TCP connectivity isn't established inside -// the main thread of the Agent +// Assert that Active TCP connectivity isn't established inside. +// the main thread of the Agent. func TestActiveTCP_NonBlocking(t *testing.T) { defer test.CheckRoutines(t)() @@ -219,7 +225,7 @@ func TestActiveTCP_NonBlocking(t *testing.T) { <-isConnected } -// Assert that we ignore remote TCP candidates when running a UDP Only Agent +// Assert that we ignore remote TCP candidates when running a UDP Only Agent. func TestActiveTCP_Respect_NetworkTypes(t *testing.T) { defer test.CheckRoutines(t)() defer test.TimeOut(time.Second * 5).Stop() @@ -271,7 +277,9 @@ func TestActiveTCP_Respect_NetworkTypes(t *testing.T) { }) require.NoError(t, err) - invalidCandidate, err := UnmarshalCandidate(fmt.Sprintf("1052353102 1 tcp 1675624447 127.0.0.1 %s typ host tcptype passive", port)) + invalidCandidate, err := UnmarshalCandidate( + fmt.Sprintf("1052353102 1 tcp 1675624447 127.0.0.1 %s typ host tcptype passive", port), + ) require.NoError(t, err) require.NoError(t, aAgent.AddRemoteCandidate(invalidCandidate)) require.NoError(t, bAgent.AddRemoteCandidate(invalidCandidate)) diff --git a/addr.go b/addr.go index fb40061..fad58b9 100644 --- a/addr.go +++ b/addr.go @@ -16,6 +16,7 @@ func addrWithOptionalZone(addr netip.Addr, zone string) netip.Addr { if addr.Is6() && (addr.IsLinkLocalUnicast() || addr.IsLinkLocalMulticast()) { return addr.WithZone(zone) } + return addr } @@ -30,22 +31,25 @@ func parseAddrFromIface(in net.Addr, ifcName string) (netip.Addr, int, NetworkTy // net.IPNet does not have a Zone but we provide it from the interface addr = addrWithOptionalZone(addr, ifcName) } + return addr, port, nt, nil } -func parseAddr(in net.Addr) (netip.Addr, int, NetworkType, error) { +func parseAddr(in net.Addr) (netip.Addr, int, NetworkType, error) { //nolint:cyclop switch addr := in.(type) { case *net.IPNet: ipAddr, err := ipAddrToNetIP(addr.IP, "") if err != nil { return netip.Addr{}, 0, 0, err } + return ipAddr, 0, 0, nil case *net.IPAddr: ipAddr, err := ipAddrToNetIP(addr.IP, addr.Zone) if err != nil { return netip.Addr{}, 0, 0, err } + return ipAddr, 0, 0, nil case *net.UDPAddr: ipAddr, err := ipAddrToNetIP(addr.IP, addr.Zone) @@ -58,6 +62,7 @@ func parseAddr(in net.Addr) (netip.Addr, int, NetworkType, error) { } else { nt = NetworkTypeUDP6 } + return ipAddr, addr.Port, nt, nil case *net.TCPAddr: ipAddr, err := ipAddrToNetIP(addr.IP, addr.Zone) @@ -70,6 +75,7 @@ func parseAddr(in net.Addr) (netip.Addr, int, NetworkType, error) { } else { nt = NetworkTypeTCP6 } + return ipAddr, addr.Port, nt, nil default: return netip.Addr{}, 0, 0, addrParseError{in} @@ -100,6 +106,7 @@ func ipAddrToNetIP(ip []byte, zone string) (netip.Addr, error) { // we'd rather have an IPv4-mapped IPv6 become IPv4 so that it is usable. netIPAddr = netIPAddr.Unmap() netIPAddr = addrWithOptionalZone(netIPAddr, zone) + return netIPAddr, nil } @@ -134,12 +141,13 @@ func toAddrPort(addr net.Addr) AddrPort { switch addr := addr.(type) { case *net.UDPAddr: copy(ap[:16], addr.IP.To16()) - ap[16] = uint8(addr.Port >> 8) - ap[17] = uint8(addr.Port) + ap[16] = uint8(addr.Port >> 8) //nolint:gosec // G115 false positive + ap[17] = uint8(addr.Port) //nolint:gosec // G115 false positive case *net.TCPAddr: copy(ap[:16], addr.IP.To16()) - ap[16] = uint8(addr.Port >> 8) - ap[17] = uint8(addr.Port) + ap[16] = uint8(addr.Port >> 8) //nolint:gosec // G115 false positive + ap[17] = uint8(addr.Port) //nolint:gosec // G115 false positive } + return ap } diff --git a/agent.go b/agent.go index 1346771..4ce1b1f 100644 --- a/agent.go +++ b/agent.go @@ -35,7 +35,7 @@ type bindingRequest struct { isUseCandidate bool } -// Agent represents the ICE agent +// Agent represents the ICE agent. type Agent struct { loop *taskloop.Loop @@ -149,8 +149,8 @@ type Agent struct { enableUseCandidateCheckPriority bool } -// NewAgent creates a new Agent -func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit +// NewAgent creates a new Agent. +func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit,cyclop var err error if config.PortMax < config.PortMin { return nil, ErrPort @@ -180,7 +180,7 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit startedCtx, startedFn := context.WithCancel(context.Background()) - a := &Agent{ + agent := &Agent{ tieBreaker: globalMathRandomGenerator.Uint64(), lite: config.Lite, gatheringState: GatheringStateNew, @@ -224,34 +224,46 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit enableUseCandidateCheckPriority: config.EnableUseCandidateCheckPriority, } - a.connectionStateNotifier = &handlerNotifier{connectionStateFunc: a.onConnectionStateChange, done: make(chan struct{})} - a.candidateNotifier = &handlerNotifier{candidateFunc: a.onCandidate, done: make(chan struct{})} - a.selectedCandidatePairNotifier = &handlerNotifier{candidatePairFunc: a.onSelectedCandidatePairChange, done: make(chan struct{})} + agent.connectionStateNotifier = &handlerNotifier{ + connectionStateFunc: agent.onConnectionStateChange, + done: make(chan struct{}), + } + agent.candidateNotifier = &handlerNotifier{candidateFunc: agent.onCandidate, done: make(chan struct{})} + agent.selectedCandidatePairNotifier = &handlerNotifier{ + candidatePairFunc: agent.onSelectedCandidatePairChange, + done: make(chan struct{}), + } - if a.net == nil { - a.net, err = stdnet.NewNet() + if agent.net == nil { + agent.net, err = stdnet.NewNet() if err != nil { return nil, fmt.Errorf("failed to create network: %w", err) } - } else if _, isVirtual := a.net.(*vnet.Net); isVirtual { - a.log.Warn("Virtual network is enabled") - if a.mDNSMode != MulticastDNSModeDisabled { - a.log.Warn("Virtual network does not support mDNS yet") + } else if _, isVirtual := agent.net.(*vnet.Net); isVirtual { + agent.log.Warn("Virtual network is enabled") + if agent.mDNSMode != MulticastDNSModeDisabled { + agent.log.Warn("Virtual network does not support mDNS yet") } } - localIfcs, _, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, a.networkTypes, a.includeLoopback) + localIfcs, _, err := localInterfaces( + agent.net, + agent.interfaceFilter, + agent.ipFilter, + agent.networkTypes, + agent.includeLoopback, + ) if err != nil { return nil, fmt.Errorf("error getting local interfaces: %w", err) } // Opportunistic mDNS: If we can't open the connection, that's ok: we // can continue without it. - if a.mDNSConn, a.mDNSMode, err = createMulticastDNS( - a.net, - a.networkTypes, + if agent.mDNSConn, agent.mDNSMode, err = createMulticastDNS( + agent.net, + agent.networkTypes, localIfcs, - a.includeLoopback, + agent.includeLoopback, mDNSMode, mDNSName, log, @@ -259,54 +271,60 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit log.Warnf("Failed to initialize mDNS %s: %v", mDNSName, err) } - config.initWithDefaults(a) + config.initWithDefaults(agent) // Make sure the buffer doesn't grow indefinitely. // NOTE: We actually won't get anywhere close to this limit. // SRTP will constantly read from the endpoint and drop packets if it's full. - a.buf.SetLimitSize(maxBufferSize) + agent.buf.SetLimitSize(maxBufferSize) + + if agent.lite && (len(agent.candidateTypes) != 1 || agent.candidateTypes[0] != CandidateTypeHost) { + agent.closeMulticastConn() - if a.lite && (len(a.candidateTypes) != 1 || a.candidateTypes[0] != CandidateTypeHost) { - a.closeMulticastConn() return nil, ErrLiteUsingNonHostCandidates } - if len(config.Urls) > 0 && !containsCandidateType(CandidateTypeServerReflexive, a.candidateTypes) && !containsCandidateType(CandidateTypeRelay, a.candidateTypes) { - a.closeMulticastConn() + if len(config.Urls) > 0 && + !containsCandidateType(CandidateTypeServerReflexive, agent.candidateTypes) && + !containsCandidateType(CandidateTypeRelay, agent.candidateTypes) { + agent.closeMulticastConn() + return nil, ErrUselessUrlsProvided } - if err = config.initExtIPMapping(a); err != nil { - a.closeMulticastConn() + if err = config.initExtIPMapping(agent); err != nil { + agent.closeMulticastConn() + return nil, err } - a.loop = taskloop.New(func() { - a.removeUfragFromMux() - a.deleteAllCandidates() - a.startedFn() + agent.loop = taskloop.New(func() { + agent.removeUfragFromMux() + agent.deleteAllCandidates() + agent.startedFn() - if err := a.buf.Close(); err != nil { - a.log.Warnf("Failed to close buffer: %v", err) + if err := agent.buf.Close(); err != nil { + agent.log.Warnf("Failed to close buffer: %v", err) } - a.closeMulticastConn() - a.updateConnectionState(ConnectionStateClosed) + agent.closeMulticastConn() + agent.updateConnectionState(ConnectionStateClosed) - a.gatherCandidateCancel() - if a.gatherCandidateDone != nil { - <-a.gatherCandidateDone + agent.gatherCandidateCancel() + if agent.gatherCandidateDone != nil { + <-agent.gatherCandidateDone } }) // Restart is also used to initialize the agent for the first time - if err := a.Restart(config.LocalUfrag, config.LocalPwd); err != nil { - a.closeMulticastConn() - _ = a.Close() + if err := agent.Restart(config.LocalUfrag, config.LocalPwd); err != nil { + agent.closeMulticastConn() + _ = agent.Close() + return nil, err } - return a, nil + return agent, nil } func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remotePwd string) error { @@ -348,7 +366,7 @@ func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remoteP }) } -func (a *Agent) connectivityChecks() { +func (a *Agent) connectivityChecks() { //nolint:cyclop lastConnectionState := ConnectionState(0) checkingDuration := time.Time{} @@ -372,6 +390,7 @@ func (a *Agent) connectivityChecks() { // We have been in checking longer then Disconnect+Failed timeout, set the connection to Failed if time.Since(checkingDuration) > a.disconnectedTimeout+a.failedTimeout { a.updateConnectionState(ConnectionStateFailed) + return } default: @@ -383,8 +402,8 @@ func (a *Agent) connectivityChecks() { } } - t := time.NewTimer(math.MaxInt64) - t.Stop() + timer := time.NewTimer(math.MaxInt64) + timer.Stop() for { interval := defaultKeepaliveInterval @@ -406,18 +425,19 @@ func (a *Agent) connectivityChecks() { updateInterval(a.disconnectedTimeout) updateInterval(a.failedTimeout) - t.Reset(interval) + timer.Reset(interval) select { case <-a.forceCandidateContact: - if !t.Stop() { - <-t.C + if !timer.Stop() { + <-timer.C } contact() - case <-t.C: + case <-timer.C: contact() case <-a.loop.Done(): - t.Stop() + timer.Stop() + return } } @@ -440,22 +460,23 @@ func (a *Agent) updateConnectionState(newState ConnectionState) { } } -func (a *Agent) setSelectedPair(p *CandidatePair) { - if p == nil { +func (a *Agent) setSelectedPair(pair *CandidatePair) { + if pair == nil { var nilPair *CandidatePair a.selectedPair.Store(nilPair) a.log.Tracef("Unset selected candidate pair") + return } - p.nominated = true - a.selectedPair.Store(p) - a.log.Tracef("Set selected candidate pair: %s", p) + pair.nominated = true + a.selectedPair.Store(pair) + a.log.Tracef("Set selected candidate pair: %s", pair) a.updateConnectionState(ConnectionStateConnected) // Notify when the selected pair changes - a.selectedCandidatePairNotifier.EnqueueSelectedCandidatePair(p) + a.selectedCandidatePairNotifier.EnqueueSelectedCandidatePair(pair) // Signal connected a.onConnectedOnce.Do(func() { close(a.onConnected) }) @@ -498,6 +519,7 @@ func (a *Agent) getBestAvailableCandidatePair() *CandidatePair { best = p } } + return best } @@ -514,12 +536,14 @@ func (a *Agent) getBestValidCandidatePair() *CandidatePair { best = p } } + return best } func (a *Agent) addPair(local, remote Candidate) *CandidatePair { p := newCandidatePair(local, remote, a.isControlling) a.checklist = append(a.checklist, p) + return p } @@ -529,6 +553,7 @@ func (a *Agent) findPair(local, remote Candidate) *CandidatePair { return p } } + return nil } @@ -578,68 +603,76 @@ func (a *Agent) checkKeepalive() { } } -// AddRemoteCandidate adds a new remote candidate -func (a *Agent) AddRemoteCandidate(c Candidate) error { - if c == nil { +// AddRemoteCandidate adds a new remote candidate. +func (a *Agent) AddRemoteCandidate(cand Candidate) error { + if cand == nil { return nil } // TCP Candidates with TCP type active will probe server passive ones, so // no need to do anything with them. - if c.TCPType() == TCPTypeActive { - a.log.Infof("Ignoring remote candidate with tcpType active: %s", c) + if cand.TCPType() == TCPTypeActive { + a.log.Infof("Ignoring remote candidate with tcpType active: %s", cand) + return nil } // If we have a mDNS Candidate lets fully resolve it before adding it locally - if c.Type() == CandidateTypeHost && strings.HasSuffix(c.Address(), ".local") { + if cand.Type() == CandidateTypeHost && strings.HasSuffix(cand.Address(), ".local") { if a.mDNSMode == MulticastDNSModeDisabled { - a.log.Warnf("Remote mDNS candidate added, but mDNS is disabled: (%s)", c.Address()) + a.log.Warnf("Remote mDNS candidate added, but mDNS is disabled: (%s)", cand.Address()) + return nil } - hostCandidate, ok := c.(*CandidateHost) + hostCandidate, ok := cand.(*CandidateHost) if !ok { return ErrAddressParseFailed } go a.resolveAndAddMulticastCandidate(hostCandidate) + return nil } go func() { if err := a.loop.Run(a.loop, func(_ context.Context) { // nolint: contextcheck - a.addRemoteCandidate(c) + a.addRemoteCandidate(cand) }); err != nil { - a.log.Warnf("Failed to add remote candidate %s: %v", c.Address(), err) + a.log.Warnf("Failed to add remote candidate %s: %v", cand.Address(), err) + return } }() + return nil } -func (a *Agent) resolveAndAddMulticastCandidate(c *CandidateHost) { +func (a *Agent) resolveAndAddMulticastCandidate(cand *CandidateHost) { if a.mDNSConn == nil { return } - _, src, err := a.mDNSConn.QueryAddr(c.context(), c.Address()) + _, src, err := a.mDNSConn.QueryAddr(cand.context(), cand.Address()) if err != nil { - a.log.Warnf("Failed to discover mDNS candidate %s: %v", c.Address(), err) + a.log.Warnf("Failed to discover mDNS candidate %s: %v", cand.Address(), err) + return } - if err = c.setIPAddr(src); err != nil { - a.log.Warnf("Failed to discover mDNS candidate %s: %v", c.Address(), err) + if err = cand.setIPAddr(src); err != nil { + a.log.Warnf("Failed to discover mDNS candidate %s: %v", cand.Address(), err) + return } if err = a.loop.Run(a.loop, func(_ context.Context) { // nolint: contextcheck - a.addRemoteCandidate(c) + a.addRemoteCandidate(cand) }); err != nil { - a.log.Warnf("Failed to add mDNS candidate %s: %v", c.Address(), err) + a.log.Warnf("Failed to add mDNS candidate %s: %v", cand.Address(), err) + return } } @@ -652,9 +685,16 @@ func (a *Agent) requestConnectivityCheck() { } func (a *Agent) addRemotePassiveTCPCandidate(remoteCandidate Candidate) { - _, localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{remoteCandidate.NetworkType()}, a.includeLoopback) + _, localIPs, err := localInterfaces( + a.net, + a.interfaceFilter, + a.ipFilter, + []NetworkType{remoteCandidate.NetworkType()}, + a.includeLoopback, + ) if err != nil { a.log.Warnf("Failed to iterate local interfaces, host candidates will not be gathered %s", err) + return } @@ -662,19 +702,21 @@ func (a *Agent) addRemotePassiveTCPCandidate(remoteCandidate Candidate) { ip, _, _, err := parseAddr(remoteCandidate.addr()) if err != nil { a.log.Warnf("Failed to parse address: %s; error: %s", remoteCandidate.addr(), err) + continue } conn := newActiveTCPConn( a.loop, net.JoinHostPort(localIPs[i].String(), "0"), - netip.AddrPortFrom(ip, uint16(remoteCandidate.Port())), + netip.AddrPortFrom(ip, uint16(remoteCandidate.Port())), //nolint:gosec // G115, no overflow, a port a.log, ) tcpAddr, ok := conn.LocalAddr().(*net.TCPAddr) if !ok { closeConnAndLog(conn, a.log, "Failed to create Active ICE-TCP Candidate: %v", errInvalidAddress) + continue } @@ -687,48 +729,52 @@ func (a *Agent) addRemotePassiveTCPCandidate(remoteCandidate Candidate) { }) if err != nil { closeConnAndLog(conn, a.log, "Failed to create Active ICE-TCP Candidate: %v", err) + continue } localCandidate.start(a, conn, a.startedCh) - a.localCandidates[localCandidate.NetworkType()] = append(a.localCandidates[localCandidate.NetworkType()], localCandidate) + a.localCandidates[localCandidate.NetworkType()] = append( + a.localCandidates[localCandidate.NetworkType()], + localCandidate, + ) a.candidateNotifier.EnqueueCandidate(localCandidate) a.addPair(localCandidate, remoteCandidate) } } -// addRemoteCandidate assumes you are holding the lock (must be execute using a.run) -func (a *Agent) addRemoteCandidate(c Candidate) { - set := a.remoteCandidates[c.NetworkType()] +// addRemoteCandidate assumes you are holding the lock (must be execute using a.run). +func (a *Agent) addRemoteCandidate(cand Candidate) { //nolint:cyclop + set := a.remoteCandidates[cand.NetworkType()] for _, candidate := range set { - if candidate.Equal(c) { + if candidate.Equal(cand) { return } } acceptRemotePassiveTCPCandidate := false // Assert that TCP4 or TCP6 is a enabled NetworkType locally - if !a.disableActiveTCP && c.TCPType() == TCPTypePassive { + if !a.disableActiveTCP && cand.TCPType() == TCPTypePassive { for _, networkType := range a.networkTypes { - if c.NetworkType() == networkType { + if cand.NetworkType() == networkType { acceptRemotePassiveTCPCandidate = true } } } if acceptRemotePassiveTCPCandidate { - a.addRemotePassiveTCPCandidate(c) + a.addRemotePassiveTCPCandidate(cand) } - set = append(set, c) - a.remoteCandidates[c.NetworkType()] = set + set = append(set, cand) + a.remoteCandidates[cand.NetworkType()] = set - if c.TCPType() != TCPTypePassive { - if localCandidates, ok := a.localCandidates[c.NetworkType()]; ok { + if cand.TCPType() != TCPTypePassive { + if localCandidates, ok := a.localCandidates[cand.NetworkType()]; ok { for _, localCandidate := range localCandidates { - a.addPair(localCandidate, c) + a.addPair(localCandidate, cand) } } } @@ -736,42 +782,43 @@ func (a *Agent) addRemoteCandidate(c Candidate) { a.requestConnectivityCheck() } -func (a *Agent) addCandidate(ctx context.Context, c Candidate, candidateConn net.PacketConn) error { +func (a *Agent) addCandidate(ctx context.Context, cand Candidate, candidateConn net.PacketConn) error { return a.loop.Run(ctx, func(context.Context) { - set := a.localCandidates[c.NetworkType()] + set := a.localCandidates[cand.NetworkType()] for _, candidate := range set { - if candidate.Equal(c) { - a.log.Debugf("Ignore duplicate candidate: %s", c) - if err := c.close(); err != nil { + if candidate.Equal(cand) { + a.log.Debugf("Ignore duplicate candidate: %s", cand) + if err := cand.close(); err != nil { a.log.Warnf("Failed to close duplicate candidate: %v", err) } if err := candidateConn.Close(); err != nil { a.log.Warnf("Failed to close duplicate candidate connection: %v", err) } + return } } - c.start(a, candidateConn, a.startedCh) + cand.start(a, candidateConn, a.startedCh) - set = append(set, c) - a.localCandidates[c.NetworkType()] = set + set = append(set, cand) + a.localCandidates[cand.NetworkType()] = set - if remoteCandidates, ok := a.remoteCandidates[c.NetworkType()]; ok { + if remoteCandidates, ok := a.remoteCandidates[cand.NetworkType()]; ok { for _, remoteCandidate := range remoteCandidates { - a.addPair(c, remoteCandidate) + a.addPair(cand, remoteCandidate) } } a.requestConnectivityCheck() - if !c.filterForLocationTracking() { - a.candidateNotifier.EnqueueCandidate(c) + if !cand.filterForLocationTracking() { + a.candidateNotifier.EnqueueCandidate(cand) } }) } -// GetRemoteCandidates returns the remote candidates +// GetRemoteCandidates returns the remote candidates. func (a *Agent) GetRemoteCandidates() ([]Candidate, error) { var res []Candidate @@ -789,7 +836,7 @@ func (a *Agent) GetRemoteCandidates() ([]Candidate, error) { return res, nil } -// GetLocalCandidates returns the local candidates +// GetLocalCandidates returns the local candidates. func (a *Agent) GetLocalCandidates() ([]Candidate, error) { var res []Candidate @@ -812,7 +859,7 @@ func (a *Agent) GetLocalCandidates() ([]Candidate, error) { return res, nil } -// GetLocalUserCredentials returns the local user credentials +// GetLocalUserCredentials returns the local user credentials. func (a *Agent) GetLocalUserCredentials() (frag string, pwd string, err error) { valSet := make(chan struct{}) err = a.loop.Run(a.loop, func(_ context.Context) { @@ -824,10 +871,11 @@ func (a *Agent) GetLocalUserCredentials() (frag string, pwd string, err error) { if err == nil { <-valSet } + return } -// GetRemoteUserCredentials returns the remote user credentials +// GetRemoteUserCredentials returns the remote user credentials. func (a *Agent) GetRemoteUserCredentials() (frag string, pwd string, err error) { valSet := make(chan struct{}) err = a.loop.Run(a.loop, func(_ context.Context) { @@ -839,6 +887,7 @@ func (a *Agent) GetRemoteUserCredentials() (frag string, pwd string, err error) if err == nil { <-valSet } + return } @@ -854,7 +903,7 @@ func (a *Agent) removeUfragFromMux() { } } -// Close cleans up the Agent +// Close cleans up the Agent. func (a *Agent) Close() error { return a.close(false) } @@ -875,13 +924,14 @@ func (a *Agent) close(graceful bool) error { a.connectionStateNotifier.Close(graceful) a.candidateNotifier.Close(graceful) a.selectedCandidatePairNotifier.Close(graceful) + return nil } // Remove all candidates. This closes any listening sockets // and removes both the local and remote candidate lists. // -// This is used for restarts, failures and on close +// This is used for restarts, failures and on close. func (a *Agent) deleteAllCandidates() { for net, cs := range a.localCandidates { for _, c := range cs { @@ -905,6 +955,7 @@ func (a *Agent) findRemoteCandidate(networkType NetworkType, addr net.Addr) Cand ip, port, _, err := parseAddr(addr) if err != nil { a.log.Warnf("Failed to parse address: %s; error: %s", addr, err) + return nil } @@ -914,6 +965,7 @@ func (a *Agent) findRemoteCandidate(networkType NetworkType, addr net.Addr) Cand return c } } + return nil } @@ -937,6 +989,7 @@ func (a *Agent) sendBindingSuccess(m *stun.Message, local, remote Candidate) { ip, port, _, err := parseAddr(base.addr()) if err != nil { a.log.Warnf("Failed to parse address: %s; error: %s", base.addr(), err) + return } @@ -976,70 +1029,85 @@ func (a *Agent) invalidatePendingBindingRequests(filterTime time.Time) { } // Assert that the passed TransactionID is in our pendingBindingRequests and returns the destination -// If the bindingRequest was valid remove it from our pending cache +// If the bindingRequest was valid remove it from our pending cache. func (a *Agent) handleInboundBindingSuccess(id [stun.TransactionIDSize]byte) (bool, *bindingRequest, time.Duration) { a.invalidatePendingBindingRequests(time.Now()) for i := range a.pendingBindingRequests { if a.pendingBindingRequests[i].transactionID == id { validBindingRequest := a.pendingBindingRequests[i] a.pendingBindingRequests = append(a.pendingBindingRequests[:i], a.pendingBindingRequests[i+1:]...) + return true, &validBindingRequest, time.Since(validBindingRequest.timestamp) } } + return false, nil, 0 } -// handleInbound processes STUN traffic from a remote candidate -func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr) { //nolint:gocognit +// handleInbound processes STUN traffic from a remote candidate. +func (a *Agent) handleInbound(msg *stun.Message, local Candidate, remote net.Addr) { //nolint:gocognit,cyclop var err error - if m == nil || local == nil { + if msg == nil || local == nil { return } - if m.Type.Method != stun.MethodBinding || - !(m.Type.Class == stun.ClassSuccessResponse || - m.Type.Class == stun.ClassRequest || - m.Type.Class == stun.ClassIndication) { - a.log.Tracef("Unhandled STUN from %s to %s class(%s) method(%s)", remote, local, m.Type.Class, m.Type.Method) + if msg.Type.Method != stun.MethodBinding || + !(msg.Type.Class == stun.ClassSuccessResponse || + msg.Type.Class == stun.ClassRequest || + msg.Type.Class == stun.ClassIndication) { + a.log.Tracef("Unhandled STUN from %s to %s class(%s) method(%s)", remote, local, msg.Type.Class, msg.Type.Method) + return } if a.isControlling { - if m.Contains(stun.AttrICEControlling) { + if msg.Contains(stun.AttrICEControlling) { a.log.Debug("Inbound STUN message: isControlling && a.isControlling == true") + return - } else if m.Contains(stun.AttrUseCandidate) { + } else if msg.Contains(stun.AttrUseCandidate) { a.log.Debug("Inbound STUN message: useCandidate && a.isControlling == true") + return } } else { - if m.Contains(stun.AttrICEControlled) { + if msg.Contains(stun.AttrICEControlled) { a.log.Debug("Inbound STUN message: isControlled && a.isControlling == false") + return } } remoteCandidate := a.findRemoteCandidate(local.NetworkType(), remote) - if m.Type.Class == stun.ClassSuccessResponse { - if err = stun.MessageIntegrity([]byte(a.remotePwd)).Check(m); err != nil { + if msg.Type.Class == stun.ClassSuccessResponse { //nolint:nestif + if err = stun.MessageIntegrity([]byte(a.remotePwd)).Check(msg); err != nil { a.log.Warnf("Discard message from (%s), %v", remote, err) + return } if remoteCandidate == nil { a.log.Warnf("Discard success message from (%s), no such remote", remote) + return } - a.selector.HandleSuccessResponse(m, local, remoteCandidate, remote) - } else if m.Type.Class == stun.ClassRequest { - a.log.Tracef("Inbound STUN (Request) from %s to %s, useCandidate: %v", remote, local, m.Contains(stun.AttrUseCandidate)) + a.selector.HandleSuccessResponse(msg, local, remoteCandidate, remote) + } else if msg.Type.Class == stun.ClassRequest { + a.log.Tracef( + "Inbound STUN (Request) from %s to %s, useCandidate: %v", + remote, + local, + msg.Contains(stun.AttrUseCandidate), + ) - if err = stunx.AssertUsername(m, a.localUfrag+":"+a.remoteUfrag); err != nil { + if err = stunx.AssertUsername(msg, a.localUfrag+":"+a.remoteUfrag); err != nil { a.log.Warnf("Discard message from (%s), %v", remote, err) + return - } else if err = stun.MessageIntegrity([]byte(a.localPwd)).Check(m); err != nil { + } else if err = stun.MessageIntegrity([]byte(a.localPwd)).Check(msg); err != nil { a.log.Warnf("Discard message from (%s), %v", remote, err) + return } @@ -1047,6 +1115,7 @@ func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr) ip, port, networkType, err := parseAddr(remote) if err != nil { a.log.Errorf("Failed to create parse remote net.Addr when creating remote prflx candidate: %s", err) + return } @@ -1062,6 +1131,7 @@ func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr) prflxCandidate, err := NewCandidatePeerReflexive(&prflxCandidateConfig) if err != nil { a.log.Errorf("Failed to create new remote prflx candidate (%s)", err) + return } remoteCandidate = prflxCandidate @@ -1070,7 +1140,7 @@ func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr) a.addRemoteCandidate(remoteCandidate) } - a.selector.HandleBindingRequest(m, local, remoteCandidate) + a.selector.HandleBindingRequest(msg, local, remoteCandidate) } if remoteCandidate != nil { @@ -1079,7 +1149,7 @@ func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr) } // validateNonSTUNTraffic processes non STUN traffic from a remote candidate, -// and returns true if it is an actual remote candidate +// and returns true if it is an actual remote candidate. func (a *Agent) validateNonSTUNTraffic(local Candidate, remote net.Addr) (Candidate, bool) { var remoteCandidate Candidate if err := a.loop.Run(local.context(), func(context.Context) { @@ -1094,7 +1164,7 @@ func (a *Agent) validateNonSTUNTraffic(local Candidate, remote net.Addr) (Candid return remoteCandidate, remoteCandidate != nil } -// GetSelectedCandidatePair returns the selected pair or nil if there is none +// GetSelectedCandidatePair returns the selected pair or nil if there is none. func (a *Agent) GetSelectedCandidatePair() (*CandidatePair, error) { selectedPair := a.getSelectedPair() if selectedPair == nil { @@ -1130,7 +1200,7 @@ func (a *Agent) closeMulticastConn() { } } -// SetRemoteCredentials sets the credentials of the remote agent +// SetRemoteCredentials sets the credentials of the remote agent. func (a *Agent) SetRemoteCredentials(remoteUfrag, remotePwd string) error { switch { case remoteUfrag == "": @@ -1152,7 +1222,7 @@ func (a *Agent) SetRemoteCredentials(remoteUfrag, remotePwd string) error { // cancel it. // After a Restart, the user must then call GatherCandidates explicitly // to start generating new ones. -func (a *Agent) Restart(ufrag, pwd string) error { +func (a *Agent) Restart(ufrag, pwd string) error { //nolint:cyclop if ufrag == "" { var err error ufrag, err = generateUFrag() @@ -1204,6 +1274,7 @@ func (a *Agent) Restart(ufrag, pwd string) error { }); runErr != nil { return runErr } + return err } @@ -1221,6 +1292,7 @@ func (a *Agent) setGatheringState(newState GatheringState) error { } <-done + return nil } diff --git a/agent_config.go b/agent_config.go index 7c7ecf6..708aab5 100644 --- a/agent_config.go +++ b/agent_config.go @@ -14,44 +14,44 @@ import ( ) const ( - // defaultCheckInterval is the interval at which the agent performs candidate checks in the connecting phase + // defaultCheckInterval is the interval at which the agent performs candidate checks in the connecting phase. defaultCheckInterval = 200 * time.Millisecond - // keepaliveInterval used to keep candidates alive + // keepaliveInterval used to keep candidates alive. defaultKeepaliveInterval = 2 * time.Second - // defaultDisconnectedTimeout is the default time till an Agent transitions disconnected + // defaultDisconnectedTimeout is the default time till an Agent transitions disconnected. defaultDisconnectedTimeout = 5 * time.Second - // defaultFailedTimeout is the default time till an Agent transitions to failed after disconnected + // defaultFailedTimeout is the default time till an Agent transitions to failed after disconnected. defaultFailedTimeout = 25 * time.Second - // defaultHostAcceptanceMinWait is the wait time before nominating a host candidate + // defaultHostAcceptanceMinWait is the wait time before nominating a host candidate. defaultHostAcceptanceMinWait = 0 - // defaultSrflxAcceptanceMinWait is the wait time before nominating a srflx candidate + // defaultSrflxAcceptanceMinWait is the wait time before nominating a srflx candidate. defaultSrflxAcceptanceMinWait = 500 * time.Millisecond - // defaultPrflxAcceptanceMinWait is the wait time before nominating a prflx candidate + // defaultPrflxAcceptanceMinWait is the wait time before nominating a prflx candidate. defaultPrflxAcceptanceMinWait = 1000 * time.Millisecond - // defaultRelayAcceptanceMinWait is the wait time before nominating a relay candidate + // defaultRelayAcceptanceMinWait is the wait time before nominating a relay candidate. defaultRelayAcceptanceMinWait = 2000 * time.Millisecond - // defaultSTUNGatherTimeout is the wait time for STUN responses + // defaultSTUNGatherTimeout is the wait time for STUN responses. defaultSTUNGatherTimeout = 5 * time.Second - // defaultMaxBindingRequests is the maximum number of binding requests before considering a pair failed + // defaultMaxBindingRequests is the maximum number of binding requests before considering a pair failed. defaultMaxBindingRequests = 7 // TCPPriorityOffset is a number which is subtracted from the default (UDP) candidate type preference // for host, srflx and prfx candidate types. defaultTCPPriorityOffset = 27 - // maxBufferSize is the number of bytes that can be buffered before we start to error + // maxBufferSize is the number of bytes that can be buffered before we start to error. maxBufferSize = 1000 * 1000 // 1MB - // maxBindingRequestTimeout is the wait time before binding requests can be deleted + // maxBindingRequestTimeout is the wait time before binding requests can be deleted. maxBindingRequestTimeout = 4000 * time.Millisecond ) @@ -60,7 +60,7 @@ func defaultCandidateTypes() []CandidateType { } // AgentConfig collects the arguments to ice.Agent construction into -// a single structure, for future-proofness of the interface +// a single structure, for future-proofness of the interface. type AgentConfig struct { Urls []*stun.URI @@ -209,109 +209,111 @@ type AgentConfig struct { EnableUseCandidateCheckPriority bool } -// initWithDefaults populates an agent and falls back to defaults if fields are unset -func (config *AgentConfig) initWithDefaults(a *Agent) { +// initWithDefaults populates an agent and falls back to defaults if fields are unset. +func (config *AgentConfig) initWithDefaults(agent *Agent) { //nolint:cyclop if config.MaxBindingRequests == nil { - a.maxBindingRequests = defaultMaxBindingRequests + agent.maxBindingRequests = defaultMaxBindingRequests } else { - a.maxBindingRequests = *config.MaxBindingRequests + agent.maxBindingRequests = *config.MaxBindingRequests } if config.HostAcceptanceMinWait == nil { - a.hostAcceptanceMinWait = defaultHostAcceptanceMinWait + agent.hostAcceptanceMinWait = defaultHostAcceptanceMinWait } else { - a.hostAcceptanceMinWait = *config.HostAcceptanceMinWait + agent.hostAcceptanceMinWait = *config.HostAcceptanceMinWait } if config.SrflxAcceptanceMinWait == nil { - a.srflxAcceptanceMinWait = defaultSrflxAcceptanceMinWait + agent.srflxAcceptanceMinWait = defaultSrflxAcceptanceMinWait } else { - a.srflxAcceptanceMinWait = *config.SrflxAcceptanceMinWait + agent.srflxAcceptanceMinWait = *config.SrflxAcceptanceMinWait } if config.PrflxAcceptanceMinWait == nil { - a.prflxAcceptanceMinWait = defaultPrflxAcceptanceMinWait + agent.prflxAcceptanceMinWait = defaultPrflxAcceptanceMinWait } else { - a.prflxAcceptanceMinWait = *config.PrflxAcceptanceMinWait + agent.prflxAcceptanceMinWait = *config.PrflxAcceptanceMinWait } if config.RelayAcceptanceMinWait == nil { - a.relayAcceptanceMinWait = defaultRelayAcceptanceMinWait + agent.relayAcceptanceMinWait = defaultRelayAcceptanceMinWait } else { - a.relayAcceptanceMinWait = *config.RelayAcceptanceMinWait + agent.relayAcceptanceMinWait = *config.RelayAcceptanceMinWait } if config.STUNGatherTimeout == nil { - a.stunGatherTimeout = defaultSTUNGatherTimeout + agent.stunGatherTimeout = defaultSTUNGatherTimeout } else { - a.stunGatherTimeout = *config.STUNGatherTimeout + agent.stunGatherTimeout = *config.STUNGatherTimeout } if config.TCPPriorityOffset == nil { - a.tcpPriorityOffset = defaultTCPPriorityOffset + agent.tcpPriorityOffset = defaultTCPPriorityOffset } else { - a.tcpPriorityOffset = *config.TCPPriorityOffset + agent.tcpPriorityOffset = *config.TCPPriorityOffset } if config.DisconnectedTimeout == nil { - a.disconnectedTimeout = defaultDisconnectedTimeout + agent.disconnectedTimeout = defaultDisconnectedTimeout } else { - a.disconnectedTimeout = *config.DisconnectedTimeout + agent.disconnectedTimeout = *config.DisconnectedTimeout } if config.FailedTimeout == nil { - a.failedTimeout = defaultFailedTimeout + agent.failedTimeout = defaultFailedTimeout } else { - a.failedTimeout = *config.FailedTimeout + agent.failedTimeout = *config.FailedTimeout } if config.KeepaliveInterval == nil { - a.keepaliveInterval = defaultKeepaliveInterval + agent.keepaliveInterval = defaultKeepaliveInterval } else { - a.keepaliveInterval = *config.KeepaliveInterval + agent.keepaliveInterval = *config.KeepaliveInterval } if config.CheckInterval == nil { - a.checkInterval = defaultCheckInterval + agent.checkInterval = defaultCheckInterval } else { - a.checkInterval = *config.CheckInterval + agent.checkInterval = *config.CheckInterval } if len(config.CandidateTypes) == 0 { - a.candidateTypes = defaultCandidateTypes() + agent.candidateTypes = defaultCandidateTypes() } else { - a.candidateTypes = config.CandidateTypes + agent.candidateTypes = config.CandidateTypes } } -func (config *AgentConfig) initExtIPMapping(a *Agent) error { +func (config *AgentConfig) initExtIPMapping(agent *Agent) error { //nolint:cyclop var err error - a.extIPMapper, err = newExternalIPMapper(config.NAT1To1IPCandidateType, config.NAT1To1IPs) + agent.extIPMapper, err = newExternalIPMapper(config.NAT1To1IPCandidateType, config.NAT1To1IPs) if err != nil { return err } - if a.extIPMapper == nil { + if agent.extIPMapper == nil { return nil // This may happen when config.NAT1To1IPs is an empty array } - if a.extIPMapper.candidateType == CandidateTypeHost { - if a.mDNSMode == MulticastDNSModeQueryAndGather { + if agent.extIPMapper.candidateType == CandidateTypeHost { //nolint:nestif + if agent.mDNSMode == MulticastDNSModeQueryAndGather { return ErrMulticastDNSWithNAT1To1IPMapping } candiHostEnabled := false - for _, candiType := range a.candidateTypes { + for _, candiType := range agent.candidateTypes { if candiType == CandidateTypeHost { candiHostEnabled = true + break } } if !candiHostEnabled { return ErrIneffectiveNAT1To1IPMappingHost } - } else if a.extIPMapper.candidateType == CandidateTypeServerReflexive { + } else if agent.extIPMapper.candidateType == CandidateTypeServerReflexive { candiSrflxEnabled := false - for _, candiType := range a.candidateTypes { + for _, candiType := range agent.candidateTypes { if candiType == CandidateTypeServerReflexive { candiSrflxEnabled = true + break } } @@ -319,5 +321,6 @@ func (config *AgentConfig) initExtIPMapping(a *Agent) error { return ErrIneffectiveNAT1To1IPMappingSrflx } } + return nil } diff --git a/agent_get_best_valid_candidate_pair_test.go b/agent_get_best_valid_candidate_pair_test.go index 2ab2069..f9f94af 100644 --- a/agent_get_best_valid_candidate_pair_test.go +++ b/agent_get_best_valid_candidate_pair_test.go @@ -32,6 +32,8 @@ func TestAgentGetBestValidCandidatePair(t *testing.T) { } func setupTestAgentGetBestValidCandidatePair(t *testing.T) *TestAgentGetBestValidCandidatePairFixture { + t.Helper() + fixture := new(TestAgentGetBestValidCandidatePairFixture) fixture.hostLocal = newHostLocal(t) fixture.relayRemote = newRelayRemote(t) diff --git a/agent_handlers.go b/agent_handlers.go index c245f0c..823514e 100644 --- a/agent_handlers.go +++ b/agent_handlers.go @@ -5,16 +5,18 @@ package ice import "sync" -// OnConnectionStateChange sets a handler that is fired when the connection state changes +// OnConnectionStateChange sets a handler that is fired when the connection state changes. func (a *Agent) OnConnectionStateChange(f func(ConnectionState)) error { a.onConnectionStateChangeHdlr.Store(f) + return nil } -// OnSelectedCandidatePairChange sets a handler that is fired when the final candidate -// pair is selected +// OnSelectedCandidatePairChange sets a handler that is fired when the final candidate. +// pair is selected. func (a *Agent) OnSelectedCandidatePairChange(f func(Candidate, Candidate)) error { a.onSelectedCandidatePairChangeHdlr.Store(f) + return nil } @@ -22,6 +24,7 @@ func (a *Agent) OnSelectedCandidatePairChange(f func(Candidate, Candidate)) erro // the gathering process complete the last candidate is nil. func (a *Agent) OnCandidate(f func(Candidate)) error { a.onCandidateHdlr.Store(f) + return nil } @@ -73,6 +76,7 @@ func (h *handlerNotifier) Close(graceful bool) { select { case <-h.done: h.Unlock() + return default: } @@ -80,7 +84,7 @@ func (h *handlerNotifier) Close(graceful bool) { h.Unlock() } -func (h *handlerNotifier) EnqueueConnectionState(s ConnectionState) { +func (h *handlerNotifier) EnqueueConnectionState(state ConnectionState) { h.Lock() defer h.Unlock() @@ -97,6 +101,7 @@ func (h *handlerNotifier) EnqueueConnectionState(s ConnectionState) { if len(h.connectionStates) == 0 { h.running = false h.Unlock() + return } notification := h.connectionStates[0] @@ -106,7 +111,7 @@ func (h *handlerNotifier) EnqueueConnectionState(s ConnectionState) { } } - h.connectionStates = append(h.connectionStates, s) + h.connectionStates = append(h.connectionStates, state) if !h.running { h.running = true h.notifiers.Add(1) @@ -114,7 +119,7 @@ func (h *handlerNotifier) EnqueueConnectionState(s ConnectionState) { } } -func (h *handlerNotifier) EnqueueCandidate(c Candidate) { +func (h *handlerNotifier) EnqueueCandidate(cand Candidate) { h.Lock() defer h.Unlock() @@ -131,6 +136,7 @@ func (h *handlerNotifier) EnqueueCandidate(c Candidate) { if len(h.candidates) == 0 { h.running = false h.Unlock() + return } notification := h.candidates[0] @@ -140,7 +146,7 @@ func (h *handlerNotifier) EnqueueCandidate(c Candidate) { } } - h.candidates = append(h.candidates, c) + h.candidates = append(h.candidates, cand) if !h.running { h.running = true h.notifiers.Add(1) @@ -148,7 +154,7 @@ func (h *handlerNotifier) EnqueueCandidate(c Candidate) { } } -func (h *handlerNotifier) EnqueueSelectedCandidatePair(p *CandidatePair) { +func (h *handlerNotifier) EnqueueSelectedCandidatePair(pair *CandidatePair) { h.Lock() defer h.Unlock() @@ -165,6 +171,7 @@ func (h *handlerNotifier) EnqueueSelectedCandidatePair(p *CandidatePair) { if len(h.selectedCandidatePairs) == 0 { h.running = false h.Unlock() + return } notification := h.selectedCandidatePairs[0] @@ -174,7 +181,7 @@ func (h *handlerNotifier) EnqueueSelectedCandidatePair(p *CandidatePair) { } } - h.selectedCandidatePairs = append(h.selectedCandidatePairs, p) + h.selectedCandidatePairs = append(h.selectedCandidatePairs, pair) if !h.running { h.running = true h.notifiers.Add(1) diff --git a/agent_handlers_test.go b/agent_handlers_test.go index c708c09..5518f26 100644 --- a/agent_handlers_test.go +++ b/agent_handlers_test.go @@ -15,7 +15,7 @@ func TestConnectionStateNotifier(t *testing.T) { defer test.CheckRoutines(t)() updates := make(chan struct{}, 1) - c := &handlerNotifier{ + notifier := &handlerNotifier{ connectionStateFunc: func(_ ConnectionState) { updates <- struct{}{} }, @@ -24,7 +24,7 @@ func TestConnectionStateNotifier(t *testing.T) { // Enqueue all updates upfront to ensure that it // doesn't block for i := 0; i < 10000; i++ { - c.EnqueueConnectionState(ConnectionStateNew) + notifier.EnqueueConnectionState(ConnectionStateNew) } done := make(chan struct{}) go func() { @@ -39,12 +39,12 @@ func TestConnectionStateNotifier(t *testing.T) { close(done) }() <-done - c.Close(true) + notifier.Close(true) }) t.Run("TestUpdateOrdering", func(t *testing.T) { defer test.CheckRoutines(t)() updates := make(chan ConnectionState) - c := &handlerNotifier{ + notifer := &handlerNotifier{ connectionStateFunc: func(cs ConnectionState) { updates <- cs }, @@ -66,9 +66,9 @@ func TestConnectionStateNotifier(t *testing.T) { close(done) }() for i := 0; i < 10000; i++ { - c.EnqueueConnectionState(ConnectionState(i)) + notifer.EnqueueConnectionState(ConnectionState(i)) } <-done - c.Close(true) + notifer.Close(true) }) } diff --git a/agent_on_selected_candidate_pair_change_test.go b/agent_on_selected_candidate_pair_change_test.go index 6ac2149..4816db7 100644 --- a/agent_on_selected_candidate_pair_change_test.go +++ b/agent_on_selected_candidate_pair_change_test.go @@ -34,17 +34,23 @@ func TestOnSelectedCandidatePairChange(t *testing.T) { } func fixtureTestOnSelectedCandidatePairChange(t *testing.T) (*Agent, *CandidatePair) { + t.Helper() + agent, err := NewAgent(&AgentConfig{}) require.NoError(t, err) candidatePair := makeCandidatePair(t) + return agent, candidatePair } func makeCandidatePair(t *testing.T) *CandidatePair { + t.Helper() + hostLocal := newHostLocal(t) relayRemote := newRelayRemote(t) candidatePair := newCandidatePair(hostLocal, relayRemote, false) + return candidatePair } diff --git a/agent_stats.go b/agent_stats.go index b18c138..45a5629 100644 --- a/agent_stats.go +++ b/agent_stats.go @@ -8,7 +8,7 @@ import ( "time" ) -// GetCandidatePairsStats returns a list of candidate pair stats +// GetCandidatePairsStats returns a list of candidate pair stats. func (a *Agent) GetCandidatePairsStats() []CandidatePairStats { var res []CandidatePairStats err := a.loop.Run(a.loop, func(_ context.Context) { @@ -49,13 +49,15 @@ func (a *Agent) GetCandidatePairsStats() []CandidatePairStats { }) if err != nil { a.log.Errorf("Failed to get candidate pairs stats: %v", err) + return []CandidatePairStats{} } + return res } // GetSelectedCandidatePairStats returns a candidate pair stats for selected candidate pair. -// Returns false if there is no selected pair +// Returns false if there is no selected pair. func (a *Agent) GetSelectedCandidatePairStats() (CandidatePairStats, bool) { isAvailable := false var res CandidatePairStats @@ -98,33 +100,34 @@ func (a *Agent) GetSelectedCandidatePairStats() (CandidatePairStats, bool) { }) if err != nil { a.log.Errorf("Failed to get selected candidate pair stats: %v", err) + return CandidatePairStats{}, false } return res, isAvailable } -// GetLocalCandidatesStats returns a list of local candidates stats +// GetLocalCandidatesStats returns a list of local candidates stats. func (a *Agent) GetLocalCandidatesStats() []CandidateStats { var res []CandidateStats err := a.loop.Run(a.loop, func(_ context.Context) { result := make([]CandidateStats, 0, len(a.localCandidates)) for networkType, localCandidates := range a.localCandidates { - for _, c := range localCandidates { + for _, cand := range localCandidates { relayProtocol := "" - if c.Type() == CandidateTypeRelay { - if cRelay, ok := c.(*CandidateRelay); ok { + if cand.Type() == CandidateTypeRelay { + if cRelay, ok := cand.(*CandidateRelay); ok { relayProtocol = cRelay.RelayProtocol() } } stat := CandidateStats{ Timestamp: time.Now(), - ID: c.ID(), + ID: cand.ID(), NetworkType: networkType, - IP: c.Address(), - Port: c.Port(), - CandidateType: c.Type(), - Priority: c.Priority(), + IP: cand.Address(), + Port: cand.Port(), + CandidateType: cand.Type(), + Priority: cand.Priority(), // URL string RelayProtocol: relayProtocol, // Deleted bool @@ -136,12 +139,14 @@ func (a *Agent) GetLocalCandidatesStats() []CandidateStats { }) if err != nil { a.log.Errorf("Failed to get candidate pair stats: %v", err) + return []CandidateStats{} } + return res } -// GetRemoteCandidatesStats returns a list of remote candidates stats +// GetRemoteCandidatesStats returns a list of remote candidates stats. func (a *Agent) GetRemoteCandidatesStats() []CandidateStats { var res []CandidateStats err := a.loop.Run(a.loop, func(_ context.Context) { @@ -166,7 +171,9 @@ func (a *Agent) GetRemoteCandidatesStats() []CandidateStats { }) if err != nil { a.log.Errorf("Failed to get candidate pair stats: %v", err) + return []CandidateStats{} } + return res } diff --git a/agent_test.go b/agent_test.go index 4b64976..f1d7f36 100644 --- a/agent_test.go +++ b/agent_test.go @@ -33,21 +33,21 @@ func (ba *BadAddr) String() string { return "yyy" } -func TestHandlePeerReflexive(t *testing.T) { +func TestHandlePeerReflexive(t *testing.T) { //nolint:cyclop defer test.CheckRoutines(t)() // Limit runtime in case of deadlocks defer test.TimeOut(time.Second * 2).Stop() t.Run("UDP prflx candidate from handleInbound()", func(t *testing.T) { - a, err := NewAgent(&AgentConfig{}) + agent, err := NewAgent(&AgentConfig{}) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() - require.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { - a.selector = &controllingSelector{agent: a, log: a.log} + require.NoError(t, agent.loop.Run(agent.loop, func(_ context.Context) { + agent.selector = &controllingSelector{agent: agent, log: agent.log} hostConfig := CandidateHostConfig{ Network: "udp", @@ -64,25 +64,25 @@ func TestHandlePeerReflexive(t *testing.T) { remote := &net.UDPAddr{IP: net.ParseIP("172.17.0.3"), Port: 999} msg, err := stun.Build(stun.BindingRequest, stun.TransactionID, - stun.NewUsername(a.localUfrag+":"+a.remoteUfrag), + stun.NewUsername(agent.localUfrag+":"+agent.remoteUfrag), UseCandidate(), - AttrControlling(a.tieBreaker), + AttrControlling(agent.tieBreaker), PriorityAttr(local.Priority()), - stun.NewShortTermIntegrity(a.localPwd), + stun.NewShortTermIntegrity(agent.localPwd), stun.Fingerprint, ) require.NoError(t, err) // nolint: contextcheck - a.handleInbound(msg, local, remote) + agent.handleInbound(msg, local, remote) // Length of remote candidate list must be one now - if len(a.remoteCandidates) != 1 { + if len(agent.remoteCandidates) != 1 { t.Fatal("failed to add a network type to the remote candidate list") } // Length of remote candidate list for a network type must be 1 - set := a.remoteCandidates[local.NetworkType()] + set := agent.remoteCandidates[local.NetworkType()] if len(set) != 1 { t.Fatal("failed to add prflx candidate to remote candidate list") } @@ -104,14 +104,14 @@ func TestHandlePeerReflexive(t *testing.T) { }) t.Run("Bad network type with handleInbound()", func(t *testing.T) { - a, err := NewAgent(&AgentConfig{}) + agent, err := NewAgent(&AgentConfig{}) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() - require.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { - a.selector = &controllingSelector{agent: a, log: a.log} + require.NoError(t, agent.loop.Run(agent.loop, func(_ context.Context) { + agent.selector = &controllingSelector{agent: agent, log: agent.log} hostConfig := CandidateHostConfig{ Network: "tcp", @@ -127,26 +127,26 @@ func TestHandlePeerReflexive(t *testing.T) { remote := &BadAddr{} // nolint: contextcheck - a.handleInbound(nil, local, remote) + agent.handleInbound(nil, local, remote) - if len(a.remoteCandidates) != 0 { + if len(agent.remoteCandidates) != 0 { t.Fatal("bad address should not be added to the remote candidate list") } })) }) t.Run("Success from unknown remote, prflx candidate MUST only be created via Binding Request", func(t *testing.T) { - a, err := NewAgent(&AgentConfig{}) + agent, err := NewAgent(&AgentConfig{}) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() - require.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { - a.selector = &controllingSelector{agent: a, log: a.log} + require.NoError(t, agent.loop.Run(agent.loop, func(_ context.Context) { + agent.selector = &controllingSelector{agent: agent, log: agent.log} tID := [stun.TransactionIDSize]byte{} copy(tID[:], "ABC") - a.pendingBindingRequests = []bindingRequest{ + agent.pendingBindingRequests = []bindingRequest{ {time.Now(), tID, &net.UDPAddr{}, false}, } @@ -164,14 +164,14 @@ func TestHandlePeerReflexive(t *testing.T) { remote := &net.UDPAddr{IP: net.ParseIP("172.17.0.3"), Port: 999} msg, err := stun.Build(stun.BindingSuccess, stun.NewTransactionIDSetter(tID), - stun.NewShortTermIntegrity(a.remotePwd), + stun.NewShortTermIntegrity(agent.remotePwd), stun.Fingerprint, ) require.NoError(t, err) // nolint: contextcheck - a.handleInbound(msg, local, remote) - if len(a.remoteCandidates) != 0 { + agent.handleInbound(msg, local, remote) + if len(agent.remoteCandidates) != 0 { t.Fatal("unknown remote was able to create a candidate") } })) @@ -281,6 +281,7 @@ func TestConnectivityOnStartup(t *testing.T) { // Ensure accepted <-accepted + return aConn, bConn }(aAgent, bAgent) @@ -308,9 +309,9 @@ func TestConnectivityLite(t *testing.T) { MappingBehavior: vnet.EndpointIndependent, FilteringBehavior: vnet.EndpointIndependent, } - v, err := buildVNet(natType, natType) + vent, err := buildVNet(natType, natType) require.NoError(t, err, "should succeed") - defer v.close() + defer vent.close() aNotifier, aConnected := onConnected() bNotifier, bConnected := onConnected() @@ -319,7 +320,7 @@ func TestConnectivityLite(t *testing.T) { Urls: []*stun.URI{stunServerURL}, NetworkTypes: supportedNetworkTypes(), MulticastDNSMode: MulticastDNSModeDisabled, - Net: v.net0, + Net: vent.net0, } aAgent, err := NewAgent(cfg0) @@ -335,7 +336,7 @@ func TestConnectivityLite(t *testing.T) { CandidateTypes: []CandidateType{CandidateTypeHost}, NetworkTypes: supportedNetworkTypes(), MulticastDNSMode: MulticastDNSModeDisabled, - Net: v.net1, + Net: vent.net1, } bAgent, err := NewAgent(cfg1) @@ -353,7 +354,7 @@ func TestConnectivityLite(t *testing.T) { <-bConnected } -func TestInboundValidity(t *testing.T) { +func TestInboundValidity(t *testing.T) { //nolint:cyclop defer test.CheckRoutines(t)() buildMsg := func(class stun.MessageClass, username, key string) *stun.Message { @@ -381,21 +382,21 @@ func TestInboundValidity(t *testing.T) { } t.Run("Invalid Binding requests should be discarded", func(t *testing.T) { - a, err := NewAgent(&AgentConfig{}) + agent, err := NewAgent(&AgentConfig{}) if err != nil { t.Fatalf("Error constructing ice.Agent") } defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() - a.handleInbound(buildMsg(stun.ClassRequest, "invalid", a.localPwd), local, remote) - if len(a.remoteCandidates) == 1 { + agent.handleInbound(buildMsg(stun.ClassRequest, "invalid", agent.localPwd), local, remote) + if len(agent.remoteCandidates) == 1 { t.Fatal("Binding with invalid Username was able to create prflx candidate") } - a.handleInbound(buildMsg(stun.ClassRequest, a.localUfrag+":"+a.remoteUfrag, "Invalid"), local, remote) - if len(a.remoteCandidates) == 1 { + agent.handleInbound(buildMsg(stun.ClassRequest, agent.localUfrag+":"+agent.remoteUfrag, "Invalid"), local, remote) + if len(agent.remoteCandidates) == 1 { t.Fatal("Binding with invalid MessageIntegrity was able to create prflx candidate") } }) @@ -452,35 +453,35 @@ func TestInboundValidity(t *testing.T) { }) t.Run("Valid bind without fingerprint", func(t *testing.T) { - a, err := NewAgent(&AgentConfig{}) + agent, err := NewAgent(&AgentConfig{}) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() - require.NoError(t, a.loop.Run(a.loop, func(_ context.Context) { - a.selector = &controllingSelector{agent: a, log: a.log} + require.NoError(t, agent.loop.Run(agent.loop, func(_ context.Context) { + agent.selector = &controllingSelector{agent: agent, log: agent.log} msg, err := stun.Build(stun.BindingRequest, stun.TransactionID, - stun.NewUsername(a.localUfrag+":"+a.remoteUfrag), - stun.NewShortTermIntegrity(a.localPwd), + stun.NewUsername(agent.localUfrag+":"+agent.remoteUfrag), + stun.NewShortTermIntegrity(agent.localPwd), ) require.NoError(t, err) // nolint: contextcheck - a.handleInbound(msg, local, remote) - if len(a.remoteCandidates) != 1 { + agent.handleInbound(msg, local, remote) + if len(agent.remoteCandidates) != 1 { t.Fatal("Binding with valid values (but no fingerprint) was unable to create prflx candidate") } })) }) t.Run("Success with invalid TransactionID", func(t *testing.T) { - a, err := NewAgent(&AgentConfig{}) + agent, err := NewAgent(&AgentConfig{}) if err != nil { t.Fatalf("Error constructing ice.Agent") } defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() hostConfig := CandidateHostConfig{ @@ -499,13 +500,13 @@ func TestInboundValidity(t *testing.T) { tID := [stun.TransactionIDSize]byte{} copy(tID[:], "ABC") msg, err := stun.Build(stun.BindingSuccess, stun.NewTransactionIDSetter(tID), - stun.NewShortTermIntegrity(a.remotePwd), + stun.NewShortTermIntegrity(agent.remotePwd), stun.Fingerprint, ) require.NoError(t, err) - a.handleInbound(msg, local, remote) - if len(a.remoteCandidates) != 0 { + agent.handleInbound(msg, local, remote) + if len(agent.remoteCandidates) != 0 { t.Fatal("unknown remote was able to create a candidate") } }) @@ -514,35 +515,35 @@ func TestInboundValidity(t *testing.T) { func TestInvalidAgentStarts(t *testing.T) { defer test.CheckRoutines(t)() - a, err := NewAgent(&AgentConfig{}) + agent, err := NewAgent(&AgentConfig{}) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() ctx := context.Background() ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) defer cancel() - if _, err = a.Dial(ctx, "", "bar"); err != nil && !errors.Is(err, ErrRemoteUfragEmpty) { + if _, err = agent.Dial(ctx, "", "bar"); err != nil && !errors.Is(err, ErrRemoteUfragEmpty) { t.Fatal(err) } - if _, err = a.Dial(ctx, "foo", ""); err != nil && !errors.Is(err, ErrRemotePwdEmpty) { + if _, err = agent.Dial(ctx, "foo", ""); err != nil && !errors.Is(err, ErrRemotePwdEmpty) { t.Fatal(err) } - if _, err = a.Dial(ctx, "foo", "bar"); err != nil && !errors.Is(err, ErrCanceledByCaller) { + if _, err = agent.Dial(ctx, "foo", "bar"); err != nil && !errors.Is(err, ErrCanceledByCaller) { t.Fatal(err) } - if _, err = a.Dial(context.TODO(), "foo", "bar"); err != nil && !errors.Is(err, ErrMultipleStart) { + if _, err = agent.Dial(context.TODO(), "foo", "bar"); err != nil && !errors.Is(err, ErrMultipleStart) { t.Fatal(err) } } -// Assert that Agent emits Connecting/Connected/Disconnected/Failed/Closed messages -func TestConnectionStateCallback(t *testing.T) { +// Assert that Agent emits Connecting/Connected/Disconnected/Failed/Closed messages. +func TestConnectionStateCallback(t *testing.T) { //nolint:cyclop defer test.CheckRoutines(t)() defer test.TimeOut(time.Second * 5).Stop() @@ -635,18 +636,18 @@ func TestInvalidGather(t *testing.T) { }) } -func TestCandidatePairsStats(t *testing.T) { +func TestCandidatePairsStats(t *testing.T) { //nolint:cyclop defer test.CheckRoutines(t)() // Avoid deadlocks? defer test.TimeOut(1 * time.Second).Stop() - a, err := NewAgent(&AgentConfig{}) + agent, err := NewAgent(&AgentConfig{}) if err != nil { t.Fatalf("Failed to create agent: %s", err) } defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() hostConfig := &CandidateHostConfig{ @@ -711,21 +712,21 @@ func TestCandidatePairsStats(t *testing.T) { } for _, remote := range []Candidate{relayRemote, srflxRemote, prflxRemote, hostRemote} { - p := a.findPair(hostLocal, remote) + p := agent.findPair(hostLocal, remote) if p == nil { - a.addPair(hostLocal, remote) + agent.addPair(hostLocal, remote) } } - p := a.findPair(hostLocal, prflxRemote) + p := agent.findPair(hostLocal, prflxRemote) p.state = CandidatePairStateFailed for i := 0; i < 10; i++ { p.UpdateRoundTripTime(time.Duration(i+1) * time.Second) } - stats := a.GetCandidatePairsStats() + stats := agent.GetCandidatePairsStats() if len(stats) != 4 { t.Fatal("expected 4 candidate pairs stats") } @@ -789,18 +790,18 @@ func TestCandidatePairsStats(t *testing.T) { } } -func TestSelectedCandidatePairStats(t *testing.T) { +func TestSelectedCandidatePairStats(t *testing.T) { //nolint:cyclop defer test.CheckRoutines(t)() // Avoid deadlocks? defer test.TimeOut(1 * time.Second).Stop() - a, err := NewAgent(&AgentConfig{}) + agent, err := NewAgent(&AgentConfig{}) if err != nil { t.Fatalf("Failed to create agent: %s", err) } defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() hostConfig := &CandidateHostConfig{ @@ -828,23 +829,23 @@ func TestSelectedCandidatePairStats(t *testing.T) { } // no selected pair, should return not available - _, ok := a.GetSelectedCandidatePairStats() + _, ok := agent.GetSelectedCandidatePairStats() require.False(t, ok) // add pair and populate some RTT stats - p := a.findPair(hostLocal, srflxRemote) + p := agent.findPair(hostLocal, srflxRemote) if p == nil { - a.addPair(hostLocal, srflxRemote) - p = a.findPair(hostLocal, srflxRemote) + agent.addPair(hostLocal, srflxRemote) + p = agent.findPair(hostLocal, srflxRemote) } for i := 0; i < 10; i++ { p.UpdateRoundTripTime(time.Duration(i+1) * time.Second) } // set the pair as selected - a.setSelectedPair(p) + agent.setSelectedPair(p) - stats, ok := a.GetSelectedCandidatePairStats() + stats, ok := agent.GetSelectedCandidatePairStats() require.True(t, ok) if stats.LocalCandidateID != hostLocal.ID() { @@ -872,18 +873,18 @@ func TestSelectedCandidatePairStats(t *testing.T) { } } -func TestLocalCandidateStats(t *testing.T) { +func TestLocalCandidateStats(t *testing.T) { //nolint:cyclop defer test.CheckRoutines(t)() // Avoid deadlocks? defer test.TimeOut(1 * time.Second).Stop() - a, err := NewAgent(&AgentConfig{}) + agent, err := NewAgent(&AgentConfig{}) if err != nil { t.Fatalf("Failed to create agent: %s", err) } defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() hostConfig := &CandidateHostConfig{ @@ -910,9 +911,9 @@ func TestLocalCandidateStats(t *testing.T) { t.Fatalf("Failed to construct local srflx candidate: %s", err) } - a.localCandidates[NetworkTypeUDP4] = []Candidate{hostLocal, srflxLocal} + agent.localCandidates[NetworkTypeUDP4] = []Candidate{hostLocal, srflxLocal} - localStats := a.GetLocalCandidatesStats() + localStats := agent.GetLocalCandidatesStats() if len(localStats) != 2 { t.Fatalf("expected 2 local candidates stats, got %d instead", len(localStats)) } @@ -953,18 +954,18 @@ func TestLocalCandidateStats(t *testing.T) { } } -func TestRemoteCandidateStats(t *testing.T) { +func TestRemoteCandidateStats(t *testing.T) { //nolint:cyclop defer test.CheckRoutines(t)() // Avoid deadlocks? defer test.TimeOut(1 * time.Second).Stop() - a, err := NewAgent(&AgentConfig{}) + agent, err := NewAgent(&AgentConfig{}) if err != nil { t.Fatalf("Failed to create agent: %s", err) } defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() relayConfig := &CandidateRelayConfig{ @@ -1017,9 +1018,9 @@ func TestRemoteCandidateStats(t *testing.T) { t.Fatalf("Failed to construct remote host candidate: %s", err) } - a.remoteCandidates[NetworkTypeUDP4] = []Candidate{relayRemote, srflxRemote, prflxRemote, hostRemote} + agent.remoteCandidates[NetworkTypeUDP4] = []Candidate{relayRemote, srflxRemote, prflxRemote, hostRemote} - remoteStats := a.GetRemoteCandidatesStats() + remoteStats := agent.GetRemoteCandidatesStats() if len(remoteStats) != 4 { t.Fatalf("expected 4 remote candidates stats, got %d instead", len(remoteStats)) } @@ -1076,31 +1077,31 @@ func TestRemoteCandidateStats(t *testing.T) { func TestInitExtIPMapping(t *testing.T) { defer test.CheckRoutines(t)() - // a.extIPMapper should be nil by default - a, err := NewAgent(&AgentConfig{}) + // agent.extIPMapper should be nil by default + agent, err := NewAgent(&AgentConfig{}) if err != nil { t.Fatalf("Failed to create agent: %v", err) } - if a.extIPMapper != nil { - require.NoError(t, a.Close()) + if agent.extIPMapper != nil { + require.NoError(t, agent.Close()) t.Fatal("a.extIPMapper should be nil by default") } - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) // a.extIPMapper should be nil when NAT1To1IPs is a non-nil empty array - a, err = NewAgent(&AgentConfig{ + agent, err = NewAgent(&AgentConfig{ NAT1To1IPs: []string{}, NAT1To1IPCandidateType: CandidateTypeHost, }) if err != nil { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) t.Fatalf("Failed to create agent: %v", err) } - if a.extIPMapper != nil { - require.NoError(t, a.Close()) + if agent.extIPMapper != nil { + require.NoError(t, agent.Close()) t.Fatal("a.extIPMapper should be nil by default") } - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) // NewAgent should return an error when 1:1 NAT for host candidate is enabled // but the candidate type does not appear in the CandidateTypes. @@ -1150,32 +1151,38 @@ func TestBindingRequestTimeout(t *testing.T) { const expectedRemovalCount = 2 - a, err := NewAgent(&AgentConfig{}) + agent, err := NewAgent(&AgentConfig{}) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() now := time.Now() - a.pendingBindingRequests = append(a.pendingBindingRequests, bindingRequest{ + agent.pendingBindingRequests = append(agent.pendingBindingRequests, bindingRequest{ timestamp: now, // Valid }) - a.pendingBindingRequests = append(a.pendingBindingRequests, bindingRequest{ + agent.pendingBindingRequests = append(agent.pendingBindingRequests, bindingRequest{ timestamp: now.Add(-3900 * time.Millisecond), // Valid }) - a.pendingBindingRequests = append(a.pendingBindingRequests, bindingRequest{ + agent.pendingBindingRequests = append(agent.pendingBindingRequests, bindingRequest{ timestamp: now.Add(-4100 * time.Millisecond), // Invalid }) - a.pendingBindingRequests = append(a.pendingBindingRequests, bindingRequest{ + agent.pendingBindingRequests = append(agent.pendingBindingRequests, bindingRequest{ timestamp: now.Add(-75 * time.Hour), // Invalid }) - a.invalidatePendingBindingRequests(now) - require.Equal(t, expectedRemovalCount, len(a.pendingBindingRequests), "Binding invalidation due to timeout did not remove the correct number of binding requests") + agent.invalidatePendingBindingRequests(now) + + require.Equal( + t, + expectedRemovalCount, + len(agent.pendingBindingRequests), + "Binding invalidation due to timeout did not remove the correct number of binding requests", + ) } // TestAgentCredentials checks if local username fragments and passwords (if set) meet RFC standard -// and ensure it's backwards compatible with previous versions of the pion/ice +// and ensure it's backwards compatible with previous versions of the pion/ice. func TestAgentCredentials(t *testing.T) { defer test.CheckRoutines(t)() @@ -1207,7 +1214,7 @@ func TestAgentCredentials(t *testing.T) { } // Assert that Agent on Failure deletes all existing candidates -// User can then do an ICE Restart to bring agent back +// User can then do an ICE Restart to bring agent back. func TestConnectionStateFailedDeleteAllCandidates(t *testing.T) { defer test.CheckRoutines(t)() @@ -1254,7 +1261,7 @@ func TestConnectionStateFailedDeleteAllCandidates(t *testing.T) { <-done } -// Assert that the ICE Agent can go directly from Connecting -> Failed on both sides +// Assert that the ICE Agent can go directly from Connecting -> Failed on both sides. func TestConnectionStateConnectingToFailed(t *testing.T) { defer test.CheckRoutines(t)() @@ -1378,6 +1385,7 @@ func TestAgentRestart(t *testing.T) { out += c.Address() + ":" out += strconv.Itoa(c.Port()) } + return } @@ -1423,33 +1431,33 @@ func TestAgentRestart(t *testing.T) { func TestGetRemoteCredentials(t *testing.T) { var config AgentConfig - a, err := NewAgent(&config) + agent, err := NewAgent(&config) if err != nil { t.Fatalf("Error constructing ice.Agent: %v", err) } defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() - a.remoteUfrag = "remoteUfrag" - a.remotePwd = "remotePwd" + agent.remoteUfrag = "remoteUfrag" + agent.remotePwd = "remotePwd" - actualUfrag, actualPwd, err := a.GetRemoteUserCredentials() + actualUfrag, actualPwd, err := agent.GetRemoteUserCredentials() require.NoError(t, err) - require.Equal(t, actualUfrag, a.remoteUfrag) - require.Equal(t, actualPwd, a.remotePwd) + require.Equal(t, actualUfrag, agent.remoteUfrag) + require.Equal(t, actualPwd, agent.remotePwd) } func TestGetRemoteCandidates(t *testing.T) { var config AgentConfig - a, err := NewAgent(&config) + agent, err := NewAgent(&config) if err != nil { t.Fatalf("Error constructing ice.Agent: %v", err) } defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() expectedCandidates := []Candidate{} @@ -1467,10 +1475,10 @@ func TestGetRemoteCandidates(t *testing.T) { expectedCandidates = append(expectedCandidates, cand) - a.addRemoteCandidate(cand) + agent.addRemoteCandidate(cand) } - actualCandidates, err := a.GetRemoteCandidates() + actualCandidates, err := agent.GetRemoteCandidates() require.NoError(t, err) require.ElementsMatch(t, expectedCandidates, actualCandidates) } @@ -1478,12 +1486,12 @@ func TestGetRemoteCandidates(t *testing.T) { func TestGetLocalCandidates(t *testing.T) { var config AgentConfig - a, err := NewAgent(&config) + agent, err := NewAgent(&config) if err != nil { t.Fatalf("Error constructing ice.Agent: %v", err) } defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() dummyConn := &net.UDPConn{} @@ -1502,11 +1510,11 @@ func TestGetLocalCandidates(t *testing.T) { expectedCandidates = append(expectedCandidates, cand) - err = a.addCandidate(context.Background(), cand, dummyConn) + err = agent.addCandidate(context.Background(), cand, dummyConn) require.NoError(t, err) } - actualCandidates, err := a.GetLocalCandidates() + actualCandidates, err := agent.GetLocalCandidates() require.NoError(t, err) require.ElementsMatch(t, expectedCandidates, actualCandidates) } @@ -1666,7 +1674,7 @@ func TestRunTaskInSelectedCandidatePairChangeCallback(t *testing.T) { <-isTested } -// Assert that a Lite agent goes to disconnected and failed +// Assert that a Lite agent goes to disconnected and failed. func TestLiteLifecycle(t *testing.T) { defer test.CheckRoutines(t)() @@ -1818,7 +1826,7 @@ func TestGetSelectedCandidatePair(t *testing.T) { require.NoError(t, wan.Stop()) } -func TestAcceptAggressiveNomination(t *testing.T) { +func TestAcceptAggressiveNomination(t *testing.T) { //nolint:cyclop defer test.CheckRoutines(t)() defer test.TimeOut(time.Second * 30).Stop() @@ -1932,24 +1940,25 @@ func TestAcceptAggressiveNomination(t *testing.T) { bcandidates, err = bAgent.GetLocalCandidates() require.NoError(t, err) - for _, c := range bcandidates { - if c != bAgent.getSelectedPair().Local { + for _, cand := range bcandidates { + if cand != bAgent.getSelectedPair().Local { //nolint:nestif if expectNewSelectedCandidate == nil { expected_change_priority: for _, candidates := range aAgent.remoteCandidates { for _, candidate := range candidates { - if candidate.Equal(c) { + if candidate.Equal(cand) { if tc.useHigherPriority { candidate.(*CandidateHost).priorityOverride += 1000 //nolint:forcetypeassert } else { candidate.(*CandidateHost).priorityOverride -= 1000 //nolint:forcetypeassert } + break expected_change_priority } } } if tc.isExpectedToSwitch { - expectNewSelectedCandidate = c + expectNewSelectedCandidate = cand } else { expectNewSelectedCandidate = aAgent.getSelectedPair().Remote } @@ -1958,18 +1967,27 @@ func TestAcceptAggressiveNomination(t *testing.T) { change_priority: for _, candidates := range aAgent.remoteCandidates { for _, candidate := range candidates { - if candidate.Equal(c) { + if candidate.Equal(cand) { if tc.useHigherPriority { candidate.(*CandidateHost).priorityOverride += 500 //nolint:forcetypeassert } else { candidate.(*CandidateHost).priorityOverride -= 500 //nolint:forcetypeassert } + break change_priority } } } } - _, err = c.writeTo(buildMsg(stun.ClassRequest, aAgent.localUfrag+":"+aAgent.remoteUfrag, aAgent.localPwd, c.Priority()).Raw, bAgent.getSelectedPair().Remote) + _, err = cand.writeTo( + buildMsg( + stun.ClassRequest, + aAgent.localUfrag+":"+aAgent.remoteUfrag, + aAgent.localPwd, + cand.Priority(), + ).Raw, + bAgent.getSelectedPair().Remote, + ) require.NoError(t, err) } } @@ -1991,7 +2009,7 @@ func TestAcceptAggressiveNomination(t *testing.T) { require.NoError(t, wan.Stop()) } -// Close can deadlock but GracefulClose must not +// Close can deadlock but GracefulClose must not. func TestAgentGracefulCloseDeadlock(t *testing.T) { defer test.CheckRoutinesStrict(t)() defer test.TimeOut(time.Second * 5).Stop() diff --git a/agent_udpmux_test.go b/agent_udpmux_test.go index 8f4efe3..1dd70c7 100644 --- a/agent_udpmux_test.go +++ b/agent_udpmux_test.go @@ -16,7 +16,7 @@ import ( "github.com/stretchr/testify/require" ) -// TestMuxAgent is an end to end test over UDP mux, ensuring two agents could connect over mux +// TestMuxAgent is an end to end test over UDP mux, ensuring two agents could connect over mux. func TestMuxAgent(t *testing.T) { defer test.CheckRoutines(t)() @@ -58,7 +58,7 @@ func TestMuxAgent(t *testing.T) { require.NoError(t, muxedA.Close()) }() - a, err := NewAgent(&AgentConfig{ + agent, err := NewAgent(&AgentConfig{ CandidateTypes: []CandidateType{CandidateTypeHost}, NetworkTypes: supportedNetworkTypes(), }) @@ -68,10 +68,10 @@ func TestMuxAgent(t *testing.T) { if aClosed { return } - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() - conn, muxedConn := connect(a, muxedA) + conn, muxedConn := connect(agent, muxedA) pair := muxedA.getSelectedPair() require.NotNil(t, pair) diff --git a/candidate.go b/candidate.go index 9fb2b05..4eb5206 100644 --- a/candidate.go +++ b/candidate.go @@ -13,13 +13,13 @@ const ( receiveMTU = 8192 defaultLocalPreference = 65535 - // ComponentRTP indicates that the candidate is used for RTP + // ComponentRTP indicates that the candidate is used for RTP. ComponentRTP uint16 = 1 - // ComponentRTCP indicates that the candidate is used for RTCP + // ComponentRTCP indicates that the candidate is used for RTCP. ComponentRTCP ) -// Candidate represents an ICE candidate +// Candidate represents an ICE candidate. type Candidate interface { // An arbitrary string used in the freezing algorithm to // group similar candidates. It is the same for two candidates that diff --git a/candidate_base.go b/candidate_base.go index c165648..55a6ce8 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -48,12 +48,12 @@ type candidateBase struct { extensions []CandidateExtension } -// Done implements context.Context +// Done implements context.Context. func (c *candidateBase) Done() <-chan struct{} { return c.closeCh } -// Err implements context.Context +// Err implements context.Context. func (c *candidateBase) Err() error { select { case <-c.closedCh: @@ -63,17 +63,17 @@ func (c *candidateBase) Err() error { } } -// Deadline implements context.Context +// Deadline implements context.Context. func (c *candidateBase) Deadline() (deadline time.Time, ok bool) { return time.Time{}, false } -// Value implements context.Context +// Value implements context.Context. func (c *candidateBase) Value(interface{}) interface{} { return nil } -// ID returns Candidate ID +// ID returns Candidate ID. func (c *candidateBase) ID() string { return c.id } @@ -86,27 +86,27 @@ func (c *candidateBase) Foundation() string { return fmt.Sprintf("%d", crc32.ChecksumIEEE([]byte(c.Type().String()+c.address+c.networkType.String()))) } -// Address returns Candidate Address +// Address returns Candidate Address. func (c *candidateBase) Address() string { return c.address } -// Port returns Candidate Port +// Port returns Candidate Port. func (c *candidateBase) Port() int { return c.port } -// Type returns candidate type +// Type returns candidate type. func (c *candidateBase) Type() CandidateType { return c.candidateType } -// NetworkType returns candidate NetworkType +// NetworkType returns candidate NetworkType. func (c *candidateBase) NetworkType() NetworkType { return c.networkType } -// Component returns candidate component +// Component returns candidate component. func (c *candidateBase) Component() uint16 { return c.component } @@ -115,8 +115,8 @@ func (c *candidateBase) SetComponent(component uint16) { c.component = component } -// LocalPreference returns the local preference for this candidate -func (c *candidateBase) LocalPreference() uint16 { +// LocalPreference returns the local preference for this candidate. +func (c *candidateBase) LocalPreference() uint16 { //nolint:cyclop if c.NetworkType().IsTCP() { // RFC 6544, section 4.2 // @@ -182,6 +182,7 @@ func (c *candidateBase) LocalPreference() uint16 { case CandidateTypeUnspecified: return 0 } + return 0 }() @@ -191,7 +192,7 @@ func (c *candidateBase) LocalPreference() uint16 { return defaultLocalPreference } -// RelatedAddress returns *CandidateRelatedAddress +// RelatedAddress returns *CandidateRelatedAddress. func (c *candidateBase) RelatedAddress() *CandidateRelatedAddress { return c.relatedAddress } @@ -200,10 +201,11 @@ func (c *candidateBase) TCPType() TCPType { return c.tcpType } -// start runs the candidate using the provided connection +// start runs the candidate using the provided connection. func (c *candidateBase) start(a *Agent, conn net.PacketConn, initializedCh <-chan struct{}) { if c.conn != nil { c.agent().log.Warn("Can't start already started candidateBase") + return } c.currAgent = a @@ -221,7 +223,7 @@ var bufferPool = sync.Pool{ // nolint:gochecknoglobals } func (c *candidateBase) recvLoop(initializedCh <-chan struct{}) { - a := c.agent() + agent := c.agent() defer close(c.closedCh) @@ -242,8 +244,9 @@ func (c *candidateBase) recvLoop(initializedCh <-chan struct{}) { n, srcAddr, err := c.conn.ReadFrom(buf) if err != nil { if !(errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed)) { - a.log.Warnf("Failed to read from candidate %s: %v", c, err) + agent.log.Warnf("Failed to read from candidate %s: %v", c, err) } + return } @@ -254,8 +257,10 @@ func (c *candidateBase) recvLoop(initializedCh <-chan struct{}) { func (c *candidateBase) validateSTUNTrafficCache(addr net.Addr) bool { if candidate, ok := c.remoteCandidateCaches[toAddrPort(addr)]; ok { candidate.seen(false) + return true } + return false } @@ -267,48 +272,51 @@ func (c *candidateBase) addRemoteCandidateCache(candidate Candidate, srcAddr net } func (c *candidateBase) handleInboundPacket(buf []byte, srcAddr net.Addr) { - a := c.agent() + agent := c.agent() if stun.IsMessage(buf) { - m := &stun.Message{ + msg := &stun.Message{ Raw: make([]byte, len(buf)), } // Explicitly copy raw buffer so Message can own the memory. - copy(m.Raw, buf) + copy(msg.Raw, buf) + + if err := msg.Decode(); err != nil { + agent.log.Warnf("Failed to handle decode ICE from %s to %s: %v", c.addr(), srcAddr, err) - if err := m.Decode(); err != nil { - a.log.Warnf("Failed to handle decode ICE from %s to %s: %v", c.addr(), srcAddr, err) return } - if err := a.loop.Run(c, func(_ context.Context) { + if err := agent.loop.Run(c, func(_ context.Context) { // nolint: contextcheck - a.handleInbound(m, c, srcAddr) + agent.handleInbound(msg, c, srcAddr) }); err != nil { - a.log.Warnf("Failed to handle message: %v", err) + agent.log.Warnf("Failed to handle message: %v", err) } return } if !c.validateSTUNTrafficCache(srcAddr) { - remoteCandidate, valid := a.validateNonSTUNTraffic(c, srcAddr) //nolint:contextcheck + remoteCandidate, valid := agent.validateNonSTUNTraffic(c, srcAddr) //nolint:contextcheck if !valid { - a.log.Warnf("Discarded message from %s, not a valid remote candidate", c.addr()) + agent.log.Warnf("Discarded message from %s, not a valid remote candidate", c.addr()) + return } c.addRemoteCandidateCache(remoteCandidate, srcAddr) } // Note: This will return packetio.ErrFull if the buffer ever manages to fill up. - if _, err := a.buf.Write(buf); err != nil { - a.log.Warnf("Failed to write packet: %s", err) + if _, err := agent.buf.Write(buf); err != nil { + agent.log.Warnf("Failed to write packet: %s", err) + return } } -// close stops the recvLoop +// close stops the recvLoop. func (c *candidateBase) close() error { // If conn has never been started will be nil if c.Done() == nil { @@ -353,13 +361,15 @@ func (c *candidateBase) writeTo(raw []byte, dst Candidate) (int, error) { return n, err } c.agent().log.Infof("Failed to send packet: %v", err) + return n, nil } c.seen(true) + return n, nil } -// TypePreference returns the type preference for this candidate +// TypePreference returns the type preference for this candidate. func (c *candidateBase) TypePreference() uint16 { pref := c.Type().Preference() if pref == 0 { @@ -397,7 +407,7 @@ func (c *candidateBase) Priority() uint32 { (1<<0)*uint32(256-c.Component()) } -// Equal is used to compare two candidateBases +// Equal is used to compare two candidateBases. func (c *candidateBase) Equal(other Candidate) bool { if c.addr() != other.addr() { if c.addr() == nil || other.addr() == nil { @@ -416,22 +426,30 @@ func (c *candidateBase) Equal(other Candidate) bool { c.RelatedAddress().Equal(other.RelatedAddress()) } -// DeepEqual is same as Equal but also compares the extensions +// DeepEqual is same as Equal but also compares the extensions. func (c *candidateBase) DeepEqual(other Candidate) bool { return c.Equal(other) && c.extensionsEqual(other.Extensions()) } -// String makes the candidateBase printable +// String makes the candidateBase printable. func (c *candidateBase) String() string { - return fmt.Sprintf("%s %s %s%s (resolved: %v)", c.NetworkType(), c.Type(), net.JoinHostPort(c.Address(), strconv.Itoa(c.Port())), c.relatedAddress, c.resolvedAddr) + return fmt.Sprintf( + "%s %s %s%s (resolved: %v)", + c.NetworkType(), + c.Type(), + net.JoinHostPort(c.Address(), strconv.Itoa(c.Port())), + c.relatedAddress, + c.resolvedAddr, + ) } // LastReceived returns a time.Time indicating the last time -// this candidate was received +// this candidate was received. func (c *candidateBase) LastReceived() time.Time { if lastReceived, ok := c.lastReceived.Load().(time.Time); ok { return lastReceived } + return time.Time{} } @@ -440,11 +458,12 @@ func (c *candidateBase) setLastReceived(t time.Time) { } // LastSent returns a time.Time indicating the last time -// this candidate was sent +// this candidate was sent. func (c *candidateBase) LastSent() time.Time { if lastSent, ok := c.lastSent.Load().(time.Time); ok { return lastSent } + return time.Time{} } @@ -484,10 +503,11 @@ func removeZoneIDFromAddress(addr string) string { if i := strings.Index(addr, "%"); i != -1 { return addr[:i] } + return addr } -// Marshal returns the string representation of the ICECandidate +// Marshal returns the string representation of the ICECandidate. func (c *candidateBase) Marshal() string { val := c.Foundation() if val == " " { @@ -618,9 +638,7 @@ func (c *candidateBase) setExtensions(extensions []CandidateExtension) { // UnmarshalCandidate Parses a candidate from a string // https://datatracker.ietf.org/doc/html/rfc5245#section-15.1 -func UnmarshalCandidate(raw string) (Candidate, error) { - // rfc5245 - +func UnmarshalCandidate(raw string) (Candidate, error) { //nolint:cyclop pos := 0 // foundation ( 1*32ice-char ) But we allow for empty foundation, @@ -805,15 +823,16 @@ func UnmarshalCandidate(raw string) (Candidate, error) { // Read an ice-char token from the raw string // ice-char = ALPHA / DIGIT / "+" / "/" -// stop reading when a space is encountered or the end of the string -func readCandidateCharToken(raw string, start int, limit int) (string, int, error) { +// stop reading when a space is encountered or the end of the string. +func readCandidateCharToken(raw string, start int, limit int) (string, int, error) { //nolint:cyclop for i, char := range raw[start:] { if char == 0x20 { // SP return raw[start : start+i], start + i + 1, nil } if i == limit { - return "", 0, fmt.Errorf("token too long: %s expected 1x%d", raw[start:start+i], limit) //nolint: err113 // handled by caller + //nolint: err113 // handled by caller + return "", 0, fmt.Errorf("token too long: %s expected 1x%d", raw[start:start+i], limit) } if !(char >= 'A' && char <= 'Z' || @@ -828,7 +847,7 @@ func readCandidateCharToken(raw string, start int, limit int) (string, int, erro } // Read an ice string token from the raw string until a space is encountered -// Or the end of the string, we imply that ice string are UTF-8 encoded +// Or the end of the string, we imply that ice string are UTF-8 encoded. func readCandidateStringToken(raw string, start int) (string, int) { for i, char := range raw[start:] { if char == 0x20 { // SP @@ -840,7 +859,7 @@ func readCandidateStringToken(raw string, start int) (string, int) { } // Read a digit token from the raw string -// stop reading when a space is encountered or the end of the string +// stop reading when a space is encountered or the end of the string. func readCandidateDigitToken(raw string, start, limit int) (int, int, error) { var val int for i, char := range raw[start:] { @@ -849,7 +868,8 @@ func readCandidateDigitToken(raw string, start, limit int) (int, int, error) { } if i == limit { - return 0, 0, fmt.Errorf("token too long: %s expected 1x%d", raw[start:start+i], limit) //nolint: err113 // handled by caller + //nolint: err113 // handled by caller + return 0, 0, fmt.Errorf("token too long: %s expected 1x%d", raw[start:start+i], limit) } if !(char >= '0' && char <= '9') { @@ -862,7 +882,7 @@ func readCandidateDigitToken(raw string, start, limit int) (int, int, error) { return val, len(raw), nil } -// Read and validate RFC 4566 port from the raw string +// Read and validate RFC 4566 port from the raw string. func readCandidatePort(raw string, start int) (int, int, error) { port, pos, err := readCandidateDigitToken(raw, start, 5) if err != nil { @@ -878,7 +898,7 @@ func readCandidatePort(raw string, start int) (int, int, error) { // Read a byte-string token from the raw string // As defined in RFC 4566 1*(%x01-09/%x0B-0C/%x0E-FF) ;any byte except NUL, CR, or LF -// we imply that extensions byte-string are UTF-8 encoded +// we imply that extensions byte-string are UTF-8 encoded. func readCandidateByteString(raw string, start int) (string, int, error) { for i, char := range raw[start:] { if char == 0x20 { // SP @@ -952,17 +972,23 @@ func unmarshalCandidateExtensions(raw string) (extensions []CandidateExtension, for i := 0; i < len(raw); { key, next, err := readCandidateByteString(raw, i) if err != nil { - return extensions, "", fmt.Errorf("%w: failed to read key %v", errParseExtension, err) //nolint: errorlint // we wrap the error + return extensions, "", fmt.Errorf( + "%w: failed to read key %v", errParseExtension, err, //nolint: errorlint // we wrap the error + ) } i = next if i >= len(raw) { - return extensions, "", fmt.Errorf("%w: missing value for %s in %s", errParseExtension, key, raw) + return extensions, "", fmt.Errorf( + "%w: missing value for %s in %s", errParseExtension, key, raw, //nolint: errorlint // we are wrapping the error + ) } value, next, err := readCandidateByteString(raw, i) if err != nil { - return extensions, "", fmt.Errorf("%w: failed to read value %v", errParseExtension, err) //nolint: errorlint // we are wrapping the error + return extensions, "", fmt.Errorf( + "%w: failed to read value %v", errParseExtension, err, //nolint: errorlint // we are wrapping the error + ) } i = next @@ -973,5 +999,5 @@ func unmarshalCandidateExtensions(raw string) (extensions []CandidateExtension, extensions = append(extensions, CandidateExtension{key, value}) } - return + return extensions, rawTCPTypeRaw, nil } diff --git a/candidate_host.go b/candidate_host.go index c14b6a7..ac33ca3 100644 --- a/candidate_host.go +++ b/candidate_host.go @@ -8,14 +8,14 @@ import ( "strings" ) -// CandidateHost is a candidate of type host +// CandidateHost is a candidate of type host. type CandidateHost struct { candidateBase network string } -// CandidateHostConfig is the config required to create a new CandidateHost +// CandidateHostConfig is the config required to create a new CandidateHost. type CandidateHostConfig struct { CandidateID string Network string @@ -28,7 +28,7 @@ type CandidateHostConfig struct { IsLocationTracked bool } -// NewCandidateHost creates a new host candidate +// NewCandidateHost creates a new host candidate. func NewCandidateHost(config *CandidateHostConfig) (*CandidateHost, error) { candidateID := config.CandidateID @@ -36,7 +36,7 @@ func NewCandidateHost(config *CandidateHostConfig) (*CandidateHost, error) { candidateID = globalCandidateIDGenerator.Generate() } - c := &CandidateHost{ + candidateHost := &CandidateHost{ candidateBase: candidateBase{ id: candidateID, address: config.Address, @@ -58,15 +58,15 @@ func NewCandidateHost(config *CandidateHostConfig) (*CandidateHost, error) { return nil, err } - if err := c.setIPAddr(ipAddr); err != nil { + if err := candidateHost.setIPAddr(ipAddr); err != nil { return nil, err } } else { // Until mDNS candidate is resolved assume it is UDPv4 - c.candidateBase.networkType = NetworkTypeUDP4 + candidateHost.candidateBase.networkType = NetworkTypeUDP4 } - return c, nil + return candidateHost, nil } func (c *CandidateHost) setIPAddr(addr netip.Addr) error { diff --git a/candidate_peer_reflexive.go b/candidate_peer_reflexive.go index b28e9a7..9bf435c 100644 --- a/candidate_peer_reflexive.go +++ b/candidate_peer_reflexive.go @@ -15,7 +15,7 @@ type CandidatePeerReflexive struct { candidateBase } -// CandidatePeerReflexiveConfig is the config required to create a new CandidatePeerReflexive +// CandidatePeerReflexiveConfig is the config required to create a new CandidatePeerReflexive. type CandidatePeerReflexiveConfig struct { CandidateID string Network string @@ -28,7 +28,7 @@ type CandidatePeerReflexiveConfig struct { RelPort int } -// NewCandidatePeerReflexive creates a new peer reflective candidate +// NewCandidatePeerReflexive creates a new peer reflective candidate. func NewCandidatePeerReflexive(config *CandidatePeerReflexiveConfig) (*CandidatePeerReflexive, error) { ipAddr, err := netip.ParseAddr(config.Address) if err != nil { diff --git a/candidate_relay.go b/candidate_relay.go index faf281b..9e88b1a 100644 --- a/candidate_relay.go +++ b/candidate_relay.go @@ -16,7 +16,7 @@ type CandidateRelay struct { onClose func() error } -// CandidateRelayConfig is the config required to create a new CandidateRelay +// CandidateRelayConfig is the config required to create a new CandidateRelay. type CandidateRelayConfig struct { CandidateID string Network string @@ -31,7 +31,7 @@ type CandidateRelayConfig struct { OnClose func() error } -// NewCandidateRelay creates a new relay candidate +// NewCandidateRelay creates a new relay candidate. func NewCandidateRelay(config *CandidateRelayConfig) (*CandidateRelay, error) { candidateID := config.CandidateID @@ -75,7 +75,7 @@ func NewCandidateRelay(config *CandidateRelayConfig) (*CandidateRelay, error) { }, nil } -// LocalPreference returns the local preference for this candidate +// LocalPreference returns the local preference for this candidate. func (c *CandidateRelay) LocalPreference() uint16 { // These preference values come from libwebrtc // https://github.com/mozilla/libwebrtc/blob/1389c76d9c79839a2ca069df1db48aa3f2e6a1ac/p2p/base/turn_port.cc#L61 @@ -103,6 +103,7 @@ func (c *CandidateRelay) close() error { err = c.onClose() c.onClose = nil } + return err } diff --git a/candidate_server_reflexive.go b/candidate_server_reflexive.go index 85d613e..3612edb 100644 --- a/candidate_server_reflexive.go +++ b/candidate_server_reflexive.go @@ -13,7 +13,7 @@ type CandidateServerReflexive struct { candidateBase } -// CandidateServerReflexiveConfig is the config required to create a new CandidateServerReflexive +// CandidateServerReflexiveConfig is the config required to create a new CandidateServerReflexive. type CandidateServerReflexiveConfig struct { CandidateID string Network string @@ -26,7 +26,7 @@ type CandidateServerReflexiveConfig struct { RelPort int } -// NewCandidateServerReflexive creates a new server reflective candidate +// NewCandidateServerReflexive creates a new server reflective candidate. func NewCandidateServerReflexive(config *CandidateServerReflexiveConfig) (*CandidateServerReflexive, error) { ipAddr, err := netip.ParseAddr(config.Address) if err != nil { diff --git a/candidate_test.go b/candidate_test.go index 8a17d0b..caafefe 100644 --- a/candidate_test.go +++ b/candidate_test.go @@ -16,7 +16,7 @@ import ( const localhostIPStr = "127.0.0.1" func TestCandidateTypePreference(t *testing.T) { - r := require.New(t) + req := require.New(t) hostDefaultPreference := uint16(126) prflxDefaultPreference := uint16(110) @@ -53,16 +53,16 @@ func TestCandidateTypePreference(t *testing.T) { } if networkType.IsTCP() { - r.Equal(hostDefaultPreference-tcpOffset, hostCandidate.TypePreference()) - r.Equal(prflxDefaultPreference-tcpOffset, prflxCandidate.TypePreference()) - r.Equal(srflxDefaultPreference-tcpOffset, srflxCandidate.TypePreference()) + req.Equal(hostDefaultPreference-tcpOffset, hostCandidate.TypePreference()) + req.Equal(prflxDefaultPreference-tcpOffset, prflxCandidate.TypePreference()) + req.Equal(srflxDefaultPreference-tcpOffset, srflxCandidate.TypePreference()) } else { - r.Equal(hostDefaultPreference, hostCandidate.TypePreference()) - r.Equal(prflxDefaultPreference, prflxCandidate.TypePreference()) - r.Equal(srflxDefaultPreference, srflxCandidate.TypePreference()) + req.Equal(hostDefaultPreference, hostCandidate.TypePreference()) + req.Equal(prflxDefaultPreference, prflxCandidate.TypePreference()) + req.Equal(srflxDefaultPreference, srflxCandidate.TypePreference()) } - r.Equal(relayDefaultPreference, relayCandidate.TypePreference()) + req.Equal(relayDefaultPreference, relayCandidate.TypePreference()) } } } @@ -266,20 +266,27 @@ func TestCandidateFoundation(t *testing.T) { }).Foundation()) } -func mustCandidateHost(conf *CandidateHostConfig) Candidate { - cand, err := NewCandidateHost(conf) - if err != nil { - panic(err) - } - return cand -} - -func mustCandidateHostWithExtensions(t *testing.T, conf *CandidateHostConfig, extensions []CandidateExtension) Candidate { +func mustCandidateHost(t *testing.T, conf *CandidateHostConfig) Candidate { t.Helper() cand, err := NewCandidateHost(conf) if err != nil { - panic(err) + t.Fatal(err) + } + + return cand +} + +func mustCandidateHostWithExtensions( + t *testing.T, + conf *CandidateHostConfig, + extensions []CandidateExtension, +) Candidate { + t.Helper() + + cand, err := NewCandidateHost(conf) + if err != nil { + t.Fatal(err) } cand.setExtensions(extensions) @@ -287,20 +294,27 @@ func mustCandidateHostWithExtensions(t *testing.T, conf *CandidateHostConfig, ex return cand } -func mustCandidateRelay(conf *CandidateRelayConfig) Candidate { - cand, err := NewCandidateRelay(conf) - if err != nil { - panic(err) - } - return cand -} - -func mustCandidateRelayWithExtensions(t *testing.T, conf *CandidateRelayConfig, extensions []CandidateExtension) Candidate { +func mustCandidateRelay(t *testing.T, conf *CandidateRelayConfig) Candidate { t.Helper() cand, err := NewCandidateRelay(conf) if err != nil { - panic(err) + t.Fatal(err) + } + + return cand +} + +func mustCandidateRelayWithExtensions( + t *testing.T, + conf *CandidateRelayConfig, + extensions []CandidateExtension, +) Candidate { + t.Helper() + + cand, err := NewCandidateRelay(conf) + if err != nil { + t.Fatal(err) } cand.setExtensions(extensions) @@ -308,20 +322,27 @@ func mustCandidateRelayWithExtensions(t *testing.T, conf *CandidateRelayConfig, return cand } -func mustCandidateServerReflexive(conf *CandidateServerReflexiveConfig) Candidate { - cand, err := NewCandidateServerReflexive(conf) - if err != nil { - panic(err) - } - return cand -} - -func mustCandidateServerReflexiveWithExtensions(t *testing.T, conf *CandidateServerReflexiveConfig, extensions []CandidateExtension) Candidate { +func mustCandidateServerReflexive(t *testing.T, conf *CandidateServerReflexiveConfig) Candidate { t.Helper() cand, err := NewCandidateServerReflexive(conf) if err != nil { - panic(err) + t.Fatal(err) + } + + return cand +} + +func mustCandidateServerReflexiveWithExtensions( + t *testing.T, + conf *CandidateServerReflexiveConfig, + extensions []CandidateExtension, +) Candidate { + t.Helper() + + cand, err := NewCandidateServerReflexive(conf) + if err != nil { + t.Fatal(err) } cand.setExtensions(extensions) @@ -329,12 +350,16 @@ func mustCandidateServerReflexiveWithExtensions(t *testing.T, conf *CandidateSer return cand } -func mustCandidatePeerReflexiveWithExtensions(t *testing.T, conf *CandidatePeerReflexiveConfig, extensions []CandidateExtension) Candidate { +func mustCandidatePeerReflexiveWithExtensions( + t *testing.T, + conf *CandidatePeerReflexiveConfig, + extensions []CandidateExtension, +) Candidate { t.Helper() cand, err := NewCandidatePeerReflexive(conf) if err != nil { - panic(err) + t.Fatal(err) } cand.setExtensions(extensions) @@ -349,7 +374,7 @@ func TestCandidateMarshal(t *testing.T) { expectError bool }{ { - mustCandidateHost(&CandidateHostConfig{ + mustCandidateHost(t, &CandidateHostConfig{ Network: NetworkTypeUDP6.String(), Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", Port: 53987, @@ -360,7 +385,7 @@ func TestCandidateMarshal(t *testing.T) { false, }, { - mustCandidateHost(&CandidateHostConfig{ + mustCandidateHost(t, &CandidateHostConfig{ Network: NetworkTypeUDP4.String(), Address: "10.0.75.1", Port: 53634, @@ -369,7 +394,7 @@ func TestCandidateMarshal(t *testing.T) { false, }, { - mustCandidateServerReflexive(&CandidateServerReflexiveConfig{ + mustCandidateServerReflexive(t, &CandidateServerReflexiveConfig{ Network: NetworkTypeUDP4.String(), Address: "191.228.238.68", Port: 53991, @@ -395,11 +420,12 @@ func TestCandidateMarshal(t *testing.T) { {"network-cost", "10"}, }, ), + //nolint: lll "4207374052 1 tcp 1685790463 192.0.2.15 50000 typ prflx raddr 10.0.0.1 rport 12345 generation 0 network-id 2 network-cost 10", false, }, { - mustCandidateRelay(&CandidateRelayConfig{ + mustCandidateRelay(t, &CandidateRelayConfig{ Network: NetworkTypeUDP4.String(), Address: "50.0.0.1", Port: 5000, @@ -410,7 +436,7 @@ func TestCandidateMarshal(t *testing.T) { false, }, { - mustCandidateHost(&CandidateHostConfig{ + mustCandidateHost(t, &CandidateHostConfig{ Network: NetworkTypeTCP4.String(), Address: "192.168.0.196", Port: 0, @@ -420,7 +446,7 @@ func TestCandidateMarshal(t *testing.T) { false, }, { - mustCandidateHost(&CandidateHostConfig{ + mustCandidateHost(t, &CandidateHostConfig{ Network: NetworkTypeUDP4.String(), Address: "e2494022-4d9a-4c1e-a750-cc48d4f8d6ee.local", Port: 60542, @@ -429,7 +455,7 @@ func TestCandidateMarshal(t *testing.T) { }, // Missing Foundation { - mustCandidateHost(&CandidateHostConfig{ + mustCandidateHost(t, &CandidateHostConfig{ Network: NetworkTypeUDP4.String(), Address: localhostIPStr, Port: 80, @@ -440,7 +466,7 @@ func TestCandidateMarshal(t *testing.T) { false, }, { - mustCandidateHost(&CandidateHostConfig{ + mustCandidateHost(t, &CandidateHostConfig{ Network: NetworkTypeUDP4.String(), Address: localhostIPStr, Port: 80, @@ -451,7 +477,7 @@ func TestCandidateMarshal(t *testing.T) { false, }, { - mustCandidateHost(&CandidateHostConfig{ + mustCandidateHost(t, &CandidateHostConfig{ Network: NetworkTypeTCP4.String(), Address: "172.28.142.173", Port: 7686, @@ -467,8 +493,10 @@ func TestCandidateMarshal(t *testing.T) { {nil, "1938809241", true}, {nil, "1986380506 99999999 udp 2122063615 10.0.75.1 53634 typ host generation 0 network-id 2", true}, {nil, "1986380506 1 udp 99999999999 10.0.75.1 53634 typ host", true}, + //nolint: lll {nil, "4207374051 1 udp 1685790463 191.228.238.68 99999999 typ srflx raddr 192.168.0.278 rport 53991 generation 0 network-id 3", true}, {nil, "4207374051 1 udp 1685790463 191.228.238.68 53991 typ srflx raddr", true}, + //nolint: lll {nil, "4207374051 1 udp 1685790463 191.228.238.68 53991 typ srflx raddr 192.168.0.278 rport 99999999 generation 0 network-id 3", true}, {nil, "4207374051 INVALID udp 2130706431 10.0.75.1 53634 typ host", true}, {nil, "4207374051 1 udp INVALID 10.0.75.1 53634 typ host", true}, @@ -521,12 +549,19 @@ func TestCandidateMarshal(t *testing.T) { actualCandidate, err := UnmarshalCandidate(test.marshaled) if test.expectError { require.Error(t, err, "expected error", test.marshaled) + return } require.NoError(t, err) - require.Truef(t, test.candidate.Equal(actualCandidate), "%s != %s", test.candidate.String(), actualCandidate.String()) + require.Truef( + t, + test.candidate.Equal(actualCandidate), + "%s != %s", + test.candidate.String(), + actualCandidate.String(), + ) require.Equal(t, test.marshaled, actualCandidate.Marshal()) }) } @@ -573,7 +608,7 @@ func TestCandidateWriteTo(t *testing.T) { } func TestMarshalUnmarshalCandidateWithZoneID(t *testing.T) { - candidateWithZoneID := mustCandidateHost(&CandidateHostConfig{ + candidateWithZoneID := mustCandidateHost(t, &CandidateHostConfig{ Network: NetworkTypeUDP6.String(), Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a%Local Connection", Port: 53987, @@ -583,7 +618,7 @@ func TestMarshalUnmarshalCandidateWithZoneID(t *testing.T) { candidateStr := "750 0 udp 500 fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a 53987 typ host" require.Equal(t, candidateStr, candidateWithZoneID.Marshal()) - candidate := mustCandidateHost(&CandidateHostConfig{ + candidate := mustCandidateHost(t, &CandidateHostConfig{ Network: NetworkTypeUDP6.String(), Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", Port: 53987, @@ -612,6 +647,7 @@ func TestCandidateExtensionsMarshal(t *testing.T) { {"ufrag", "QNvE"}, {"network-id", "4"}, }, + //nolint: lll "1299692247 1 udp 2122134271 fdc8:cc8:c835:e400:343c:feb:32c8:17b9 58240 typ host generation 0 ufrag QNvE network-id 4", }, { @@ -620,6 +656,7 @@ func TestCandidateExtensionsMarshal(t *testing.T) { {"network-id", "2"}, {"network-cost", "50"}, }, + //nolint:lll "647372371 1 udp 1694498815 191.228.238.68 53991 typ srflx raddr 192.168.0.274 rport 53991 generation 1 network-id 2 network-cost 50", }, { @@ -628,6 +665,7 @@ func TestCandidateExtensionsMarshal(t *testing.T) { {"network-id", "2"}, {"network-cost", "10"}, }, + //nolint:lll "4207374052 1 tcp 1685790463 192.0.2.15 50000 typ prflx raddr 10.0.0.1 rport 12345 generation 0 network-id 2 network-cost 10", }, { @@ -638,6 +676,7 @@ func TestCandidateExtensionsMarshal(t *testing.T) { {"ufrag", "frag42abcdef"}, {"password", "abc123exp123"}, }, + //nolint: lll "848194626 1 udp 16777215 50.0.0.1 5000 typ relay raddr 192.168.0.1 rport 5001 generation 0 network-id 1 network-cost 20 ufrag frag42abcdef password abc123exp123", }, { @@ -703,7 +742,7 @@ func TestCandidateExtensionsDeepEqual(t *testing.T) { equal bool }{ { - mustCandidateHost(&CandidateHostConfig{ + mustCandidateHost(t, &CandidateHostConfig{ Network: NetworkTypeUDP4.String(), Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", Port: 53987, diff --git a/candidatepair.go b/candidatepair.go index 2b27eb1..e2b4d5e 100644 --- a/candidatepair.go +++ b/candidatepair.go @@ -20,8 +20,7 @@ func newCandidatePair(local, remote Candidate, controlling bool) *CandidatePair } } -// CandidatePair is a combination of a -// local and remote candidate +// CandidatePair is a combination of a local and remote candidate. type CandidatePair struct { iceRoleControlling bool Remote Candidate @@ -42,8 +41,17 @@ func (p *CandidatePair) String() string { return "" } - return fmt.Sprintf("prio %d (local, prio %d) %s <-> %s (remote, prio %d), state: %s, nominated: %v, nominateOnBindingSuccess: %v", - p.priority(), p.Local.Priority(), p.Local, p.Remote, p.Remote.Priority(), p.state, p.nominated, p.nominateOnBindingSuccess) + return fmt.Sprintf( + "prio %d (local, prio %d) %s <-> %s (remote, prio %d), state: %s, nominated: %v, nominateOnBindingSuccess: %v", + p.priority(), + p.Local.Priority(), + p.Local, + p.Remote, + p.Remote.Priority(), + p.state, + p.nominated, + p.nominateOnBindingSuccess, + ) } func (p *CandidatePair) equal(other *CandidatePair) bool { @@ -53,6 +61,7 @@ func (p *CandidatePair) equal(other *CandidatePair) bool { if p == nil || other == nil { return false } + return p.Local.Equal(other.Local) && p.Remote.Equal(other.Remote) } @@ -60,9 +69,9 @@ func (p *CandidatePair) equal(other *CandidatePair) bool { // Let G be the priority for the candidate provided by the controlling // agent. Let D be the priority for the candidate provided by the // controlled agent. -// pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0) +// pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0). func (p *CandidatePair) priority() uint64 { - var g, d uint32 + var g, d uint32 //nolint:varnamelen // clearer to use g and d here if p.iceRoleControlling { g = p.Local.Priority() d = p.Remote.Priority() @@ -77,18 +86,21 @@ func (p *CandidatePair) priority() uint64 { if x < y { return uint64(x) } + return uint64(y) } localMax := func(x, y uint32) uint64 { if x > y { return uint64(x) } + return uint64(y) } cmp := func(x, y uint32) uint64 { if x > y { return uint64(1) } + return uint64(0) } @@ -109,7 +121,7 @@ func (a *Agent) sendSTUN(msg *stun.Message, local, remote Candidate) { } // UpdateRoundTripTime sets the current round time of this pair and -// accumulates total round trip time and responses received +// accumulates total round trip time and responses received. func (p *CandidatePair) UpdateRoundTripTime(rtt time.Duration) { rttNs := rtt.Nanoseconds() atomic.StoreInt64(&p.currentRoundTripTime, rttNs) diff --git a/candidatepair_state.go b/candidatepair_state.go index b7f0dd8..e1efd39 100644 --- a/candidatepair_state.go +++ b/candidatepair_state.go @@ -3,12 +3,12 @@ package ice -// CandidatePairState represent the ICE candidate pair state +// CandidatePairState represent the ICE candidate pair state. type CandidatePairState int const ( // CandidatePairStateWaiting means a check has not been performed for - // this pair + // this pair. CandidatePairStateWaiting CandidatePairState = iota + 1 // CandidatePairStateInProgress means a check has been sent for this pair, @@ -36,5 +36,6 @@ func (c CandidatePairState) String() string { case CandidatePairStateSucceeded: return "succeeded" } + return "Unknown candidate pair state" } diff --git a/candidaterelatedaddress.go b/candidaterelatedaddress.go index e87c705..161adf8 100644 --- a/candidaterelatedaddress.go +++ b/candidaterelatedaddress.go @@ -12,7 +12,7 @@ type CandidateRelatedAddress struct { Port int } -// String makes CandidateRelatedAddress printable +// String makes CandidateRelatedAddress printable. func (c *CandidateRelatedAddress) String() string { if c == nil { return "" @@ -27,6 +27,7 @@ func (c *CandidateRelatedAddress) Equal(other *CandidateRelatedAddress) bool { if c == nil && other == nil { return true } + return c != nil && other != nil && c.Address == other.Address && c.Port == other.Port diff --git a/candidatetype.go b/candidatetype.go index 3972934..fef798b 100644 --- a/candidatetype.go +++ b/candidatetype.go @@ -3,10 +3,10 @@ package ice -// CandidateType represents the type of candidate +// CandidateType represents the type of candidate. type CandidateType byte -// CandidateType enum +// CandidateType enum. const ( CandidateTypeUnspecified CandidateType = iota CandidateTypeHost @@ -15,7 +15,7 @@ const ( CandidateTypeRelay ) -// String makes CandidateType printable +// String makes CandidateType printable. func (c CandidateType) String() string { switch c { case CandidateTypeHost: @@ -29,6 +29,7 @@ func (c CandidateType) String() string { case CandidateTypeUnspecified: return "Unknown candidate type" } + return "Unknown candidate type" } @@ -49,6 +50,7 @@ func (c CandidateType) Preference() uint16 { case CandidateTypeRelay, CandidateTypeUnspecified: return 0 } + return 0 } @@ -61,5 +63,6 @@ func containsCandidateType(candidateType CandidateType, candidateTypeList []Cand return true } } + return false } diff --git a/connectivity_vnet_test.go b/connectivity_vnet_test.go index 6a13269..5ab5b56 100644 --- a/connectivity_vnet_test.go +++ b/connectivity_vnet_test.go @@ -45,7 +45,7 @@ func (v *virtualNet) close() { v.wan.Stop() //nolint:errcheck,gosec } -func buildVNet(natType0, natType1 *vnet.NATType) (*virtualNet, error) { +func buildVNet(natType0, natType1 *vnet.NATType) (*virtualNet, error) { //nolint:cyclop loggerFactory := logging.NewDefaultLoggerFactory() // WAN @@ -77,6 +77,7 @@ func buildVNet(natType0, natType1 *vnet.NATType) (*virtualNet, error) { vnetGlobalIPA + "/" + vnetLocalIPA, } } + return []string{ vnetGlobalIPA, } @@ -114,6 +115,7 @@ func buildVNet(natType0, natType1 *vnet.NATType) (*virtualNet, error) { vnetGlobalIPB + "/" + vnetLocalIPB, } } + return []string{ vnetGlobalIPB, } @@ -175,6 +177,7 @@ func addVNetSTUN(wanNet *vnet.Net, loggerFactory logging.LoggerFactory) (*turn.S if pw, ok := credMap[username]; ok { return turn.GenerateAuthKey(username, realm, pw), true } + return nil, false }, PacketConnConfigs: []turn.PacketConnConfig{ @@ -222,6 +225,7 @@ func connectWithVNet(aAgent, bAgent *Agent) (*Conn, *Conn) { // Ensure accepted <-accepted + return aConn, bConn } @@ -230,7 +234,7 @@ type agentTestConfig struct { nat1To1IPCandidateType CandidateType } -func pipeWithVNet(v *virtualNet, a0TestConfig, a1TestConfig *agentTestConfig) (*Conn, *Conn) { +func pipeWithVNet(vnet *virtualNet, a0TestConfig, a1TestConfig *agentTestConfig) (*Conn, *Conn) { aNotifier, aConnected := onConnected() bNotifier, bConnected := onConnected() @@ -247,7 +251,7 @@ func pipeWithVNet(v *virtualNet, a0TestConfig, a1TestConfig *agentTestConfig) (* MulticastDNSMode: MulticastDNSModeDisabled, NAT1To1IPs: nat1To1IPs, NAT1To1IPCandidateType: a0TestConfig.nat1To1IPCandidateType, - Net: v.net0, + Net: vnet.net0, } aAgent, err := NewAgent(cfg0) @@ -270,7 +274,7 @@ func pipeWithVNet(v *virtualNet, a0TestConfig, a1TestConfig *agentTestConfig) (* MulticastDNSMode: MulticastDNSModeDisabled, NAT1To1IPs: nat1To1IPs, NAT1To1IPCandidateType: a1TestConfig.nat1To1IPCandidateType, - Net: v.net1, + Net: vnet.net1, } bAgent, err := NewAgent(cfg1) @@ -293,6 +297,8 @@ func pipeWithVNet(v *virtualNet, a0TestConfig, a1TestConfig *agentTestConfig) (* } func closePipe(t *testing.T, ca *Conn, cb *Conn) { + t.Helper() + require.NoError(t, ca.Close()) require.NoError(t, cb.Close()) } @@ -325,10 +331,10 @@ func TestConnectivityVNet(t *testing.T) { MappingBehavior: vnet.EndpointIndependent, FilteringBehavior: vnet.EndpointIndependent, } - v, err := buildVNet(natType, natType) + vnet, err := buildVNet(natType, natType) require.NoError(t, err, "should succeed") - defer v.close() + defer vnet.close() log.Debug("Connecting...") a0TestConfig := &agentTestConfig{ @@ -341,7 +347,7 @@ func TestConnectivityVNet(t *testing.T) { stunServerURL, }, } - ca, cb := pipeWithVNet(v, a0TestConfig, a1TestConfig) + ca, cb := pipeWithVNet(vnet, a0TestConfig, a1TestConfig) time.Sleep(1 * time.Second) @@ -358,10 +364,10 @@ func TestConnectivityVNet(t *testing.T) { MappingBehavior: vnet.EndpointAddrPortDependent, FilteringBehavior: vnet.EndpointAddrPortDependent, } - v, err := buildVNet(natType, natType) + vnet, err := buildVNet(natType, natType) require.NoError(t, err, "should succeed") - defer v.close() + defer vnet.close() log.Debug("Connecting...") a0TestConfig := &agentTestConfig{ @@ -375,7 +381,7 @@ func TestConnectivityVNet(t *testing.T) { stunServerURL, }, } - ca, cb := pipeWithVNet(v, a0TestConfig, a1TestConfig) + ca, cb := pipeWithVNet(vnet, a0TestConfig, a1TestConfig) log.Debug("Closing...") closePipe(t, ca, cb) @@ -394,10 +400,10 @@ func TestConnectivityVNet(t *testing.T) { MappingBehavior: vnet.EndpointAddrPortDependent, FilteringBehavior: vnet.EndpointAddrPortDependent, } - v, err := buildVNet(natType0, natType1) + vnet, err := buildVNet(natType0, natType1) require.NoError(t, err, "should succeed") - defer v.close() + defer vnet.close() log.Debug("Connecting...") a0TestConfig := &agentTestConfig{ @@ -407,7 +413,7 @@ func TestConnectivityVNet(t *testing.T) { a1TestConfig := &agentTestConfig{ urls: []*stun.URI{}, } - ca, cb := pipeWithVNet(v, a0TestConfig, a1TestConfig) + ca, cb := pipeWithVNet(vnet, a0TestConfig, a1TestConfig) log.Debug("Closing...") closePipe(t, ca, cb) @@ -426,10 +432,10 @@ func TestConnectivityVNet(t *testing.T) { MappingBehavior: vnet.EndpointAddrPortDependent, FilteringBehavior: vnet.EndpointAddrPortDependent, } - v, err := buildVNet(natType0, natType1) + vnet, err := buildVNet(natType0, natType1) require.NoError(t, err, "should succeed") - defer v.close() + defer vnet.close() log.Debug("Connecting...") a0TestConfig := &agentTestConfig{ @@ -439,14 +445,15 @@ func TestConnectivityVNet(t *testing.T) { a1TestConfig := &agentTestConfig{ urls: []*stun.URI{}, } - ca, cb := pipeWithVNet(v, a0TestConfig, a1TestConfig) + ca, cb := pipeWithVNet(vnet, a0TestConfig, a1TestConfig) log.Debug("Closing...") closePipe(t, ca, cb) }) } -// TestDisconnectedToConnected requires that an agent can go to disconnected, and then return to connected successfully +// TestDisconnectedToConnected requires that an agent can go to disconnected, +// and then return to connected successfully. func TestDisconnectedToConnected(t *testing.T) { defer test.CheckRoutines(t)() @@ -546,7 +553,7 @@ func TestDisconnectedToConnected(t *testing.T) { require.NoError(t, wan.Stop()) } -// Agent.Write should use the best valid pair if a selected pair is not yet available +// Agent.Write should use the best valid pair if a selected pair is not yet available. func TestWriteUseValidPair(t *testing.T) { defer test.CheckRoutines(t)() diff --git a/errors.go b/errors.go index f48be14..39b22e5 100644 --- a/errors.go +++ b/errors.go @@ -29,69 +29,71 @@ var ( ErrPort = errors.New("invalid port") // ErrLocalUfragInsufficientBits indicates local username fragment insufficient bits are provided. - // Have to be at least 24 bits long + // Have to be at least 24 bits long. ErrLocalUfragInsufficientBits = errors.New("local username fragment is less than 24 bits long") // ErrLocalPwdInsufficientBits indicates local password insufficient bits are provided. - // Have to be at least 128 bits long + // Have to be at least 128 bits long. ErrLocalPwdInsufficientBits = errors.New("local password is less than 128 bits long") // ErrProtoType indicates an unsupported transport type was provided. ErrProtoType = errors.New("invalid transport protocol type") - // ErrClosed indicates the agent is closed + // ErrClosed indicates the agent is closed. ErrClosed = taskloop.ErrClosed - // ErrNoCandidatePairs indicates agent does not have a valid candidate pair + // ErrNoCandidatePairs indicates agent does not have a valid candidate pair. ErrNoCandidatePairs = errors.New("no candidate pairs available") - // ErrCanceledByCaller indicates agent connection was canceled by the caller + // ErrCanceledByCaller indicates agent connection was canceled by the caller. ErrCanceledByCaller = errors.New("connecting canceled by caller") - // ErrMultipleStart indicates agent was started twice + // ErrMultipleStart indicates agent was started twice. ErrMultipleStart = errors.New("attempted to start agent twice") - // ErrRemoteUfragEmpty indicates agent was started with an empty remote ufrag + // ErrRemoteUfragEmpty indicates agent was started with an empty remote ufrag. ErrRemoteUfragEmpty = errors.New("remote ufrag is empty") - // ErrRemotePwdEmpty indicates agent was started with an empty remote pwd + // ErrRemotePwdEmpty indicates agent was started with an empty remote pwd. ErrRemotePwdEmpty = errors.New("remote pwd is empty") - // ErrNoOnCandidateHandler indicates agent was started without OnCandidate + // ErrNoOnCandidateHandler indicates agent was started without OnCandidate. ErrNoOnCandidateHandler = errors.New("no OnCandidate provided") - // ErrMultipleGatherAttempted indicates GatherCandidates has been called multiple times + // ErrMultipleGatherAttempted indicates GatherCandidates has been called multiple times. ErrMultipleGatherAttempted = errors.New("attempting to gather candidates during gathering state") - // ErrUsernameEmpty indicates agent was give TURN URL with an empty Username + // ErrUsernameEmpty indicates agent was give TURN URL with an empty Username. ErrUsernameEmpty = errors.New("username is empty") - // ErrPasswordEmpty indicates agent was give TURN URL with an empty Password + // ErrPasswordEmpty indicates agent was give TURN URL with an empty Password. ErrPasswordEmpty = errors.New("password is empty") - // ErrAddressParseFailed indicates we were unable to parse a candidate address + // ErrAddressParseFailed indicates we were unable to parse a candidate address. ErrAddressParseFailed = errors.New("failed to parse address") - // ErrLiteUsingNonHostCandidates indicates non host candidates were selected for a lite agent + // ErrLiteUsingNonHostCandidates indicates non host candidates were selected for a lite agent. ErrLiteUsingNonHostCandidates = errors.New("lite agents must only use host candidates") // ErrUselessUrlsProvided indicates that one or more URL was provided to the agent but no host - // candidate required them + // candidate required them. ErrUselessUrlsProvided = errors.New("agent does not need URL with selected candidate types") // ErrUnsupportedNAT1To1IPCandidateType indicates that the specified NAT1To1IPCandidateType is - // unsupported + // unsupported. ErrUnsupportedNAT1To1IPCandidateType = errors.New("unsupported 1:1 NAT IP candidate type") - // ErrInvalidNAT1To1IPMapping indicates that the given 1:1 NAT IP mapping is invalid + // ErrInvalidNAT1To1IPMapping indicates that the given 1:1 NAT IP mapping is invalid. ErrInvalidNAT1To1IPMapping = errors.New("invalid 1:1 NAT IP mapping") - // ErrExternalMappedIPNotFound in NAT1To1IPMapping + // ErrExternalMappedIPNotFound in NAT1To1IPMapping. ErrExternalMappedIPNotFound = errors.New("external mapped IP not found") // ErrMulticastDNSWithNAT1To1IPMapping indicates that the mDNS gathering cannot be used along // with 1:1 NAT IP mapping for host candidate. - ErrMulticastDNSWithNAT1To1IPMapping = errors.New("mDNS gathering cannot be used with 1:1 NAT IP mapping for host candidate") + ErrMulticastDNSWithNAT1To1IPMapping = errors.New( + "mDNS gathering cannot be used with 1:1 NAT IP mapping for host candidate", + ) // ErrIneffectiveNAT1To1IPMappingHost indicates that 1:1 NAT IP mapping for host candidate is // requested, but the host candidate type is disabled. @@ -101,10 +103,12 @@ var ( // requested, but the srflx candidate type is disabled. ErrIneffectiveNAT1To1IPMappingSrflx = errors.New("1:1 NAT IP mapping for srflx candidate ineffective") - // ErrInvalidMulticastDNSHostName indicates an invalid MulticastDNSHostName - ErrInvalidMulticastDNSHostName = errors.New("invalid mDNS HostName, must end with .local and can only contain a single '.'") + // ErrInvalidMulticastDNSHostName indicates an invalid MulticastDNSHostName. + ErrInvalidMulticastDNSHostName = errors.New( + "invalid mDNS HostName, must end with .local and can only contain a single '.'", + ) - // ErrRunCanceled indicates a run operation was canceled by its individual done + // ErrRunCanceled indicates a run operation was canceled by its individual done. ErrRunCanceled = errors.New("run was canceled by done") // ErrTCPRemoteAddrAlreadyExists indicates we already have the connection with same remote addr. @@ -113,7 +117,7 @@ var ( // ErrUnknownCandidateTyp indicates that a candidate had a unknown type value. ErrUnknownCandidateTyp = errors.New("unknown candidate typ") - // ErrDetermineNetworkType indicates that the NetworkType was not able to be parsed + // ErrDetermineNetworkType indicates that the NetworkType was not able to be parsed. ErrDetermineNetworkType = errors.New("unable to determine networkType") errAttributeTooShortICECandidate = errors.New("attribute not long enough to be ICE candidate") @@ -144,5 +148,5 @@ var ( // UDPMuxDefault should not listen on unspecified address, but to keep backward compatibility, don't return error now. // will be used in the future. - // errListenUnspecified = errors.New("can't listen on unspecified address") + // errListenUnspecified = errors.New("can't listen on unspecified address"). ) diff --git a/examples/ping-pong/main.go b/examples/ping-pong/main.go index a8117d9..5bb5ae4 100644 --- a/examples/ping-pong/main.go +++ b/examples/ping-pong/main.go @@ -26,7 +26,7 @@ var ( localHTTPPort, remoteHTTPPort int ) -// HTTP Listener to get ICE Credentials from remote Peer +// HTTP Listener to get ICE Credentials from remote Peer. func remoteAuth(_ http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { panic(err) @@ -36,7 +36,7 @@ func remoteAuth(_ http.ResponseWriter, r *http.Request) { remoteAuthChannel <- r.PostForm["pwd"][0] } -// HTTP Listener to get ICE Candidate from remote Peer +// HTTP Listener to get ICE Candidate from remote Peer. func remoteCandidate(_ http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { panic(err) diff --git a/external_ip_mapper.go b/external_ip_mapper.go index 3d542fb..2d483ff 100644 --- a/external_ip_mapper.go +++ b/external_ip_mapper.go @@ -13,10 +13,13 @@ func validateIPString(ipStr string) (net.IP, bool, error) { if ip == nil { return nil, false, ErrInvalidNAT1To1IPMapping } + return ip, (ip.To4() != nil), nil } -// ipMapping holds the mapping of local and external IP address for a particular IP family +// ipMapping holds the mapping of local and external IP address +// +// for a particular IP family. type ipMapping struct { ipSole net.IP // When non-nil, this is the sole external IP for one local IP assumed ipMap map[string]net.IP // Local-to-external IP mapping (k: local, v: external) @@ -75,7 +78,11 @@ type externalIPMapper struct { candidateType CandidateType } -func newExternalIPMapper(candidateType CandidateType, ips []string) (*externalIPMapper, error) { //nolint:gocognit +//nolint:gocognit,cyclop +func newExternalIPMapper( + candidateType CandidateType, + ips []string, +) (*externalIPMapper, error) { if len(ips) == 0 { return nil, nil //nolint:nilnil } @@ -85,7 +92,7 @@ func newExternalIPMapper(candidateType CandidateType, ips []string) (*externalIP return nil, ErrUnsupportedNAT1To1IPCandidateType } - m := &externalIPMapper{ + mapper := &externalIPMapper{ ipv4Mapping: ipMapping{ipMap: map[string]net.IP{}}, ipv6Mapping: ipMapping{ipMap: map[string]net.IP{}}, candidateType: candidateType, @@ -101,13 +108,13 @@ func newExternalIPMapper(candidateType CandidateType, ips []string) (*externalIP if err != nil { return nil, err } - if len(ipPair) == 1 { + if len(ipPair) == 1 { //nolint:nestif if isExtIPv4 { - if err := m.ipv4Mapping.setSoleIP(extIP); err != nil { + if err := mapper.ipv4Mapping.setSoleIP(extIP); err != nil { return nil, err } } else { - if err := m.ipv6Mapping.setSoleIP(extIP); err != nil { + if err := mapper.ipv6Mapping.setSoleIP(extIP); err != nil { return nil, err } } @@ -121,7 +128,7 @@ func newExternalIPMapper(candidateType CandidateType, ips []string) (*externalIP return nil, ErrInvalidNAT1To1IPMapping } - if err := m.ipv4Mapping.addIPMapping(locIP, extIP); err != nil { + if err := mapper.ipv4Mapping.addIPMapping(locIP, extIP); err != nil { return nil, err } } else { @@ -129,14 +136,14 @@ func newExternalIPMapper(candidateType CandidateType, ips []string) (*externalIP return nil, ErrInvalidNAT1To1IPMapping } - if err := m.ipv6Mapping.addIPMapping(locIP, extIP); err != nil { + if err := mapper.ipv6Mapping.addIPMapping(locIP, extIP); err != nil { return nil, err } } } } - return m, nil + return mapper, nil } func (m *externalIPMapper) findExternalIP(localIPStr string) (net.IP, error) { diff --git a/external_ip_mapper_test.go b/external_ip_mapper_test.go index 76b3d8d..bedf484 100644 --- a/external_ip_mapper_test.go +++ b/external_ip_mapper_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestExternalIPMapper(t *testing.T) { +func TestExternalIPMapper(t *testing.T) { //nolint:maintidx t.Run("validateIPString", func(t *testing.T) { var ip net.IP var isIPv4 bool @@ -31,165 +31,165 @@ func TestExternalIPMapper(t *testing.T) { }) t.Run("newExternalIPMapper", func(t *testing.T) { - var m *externalIPMapper + var mapper *externalIPMapper var err error // ips being nil should succeed but mapper will be nil also - m, err = newExternalIPMapper(CandidateTypeUnspecified, nil) + mapper, err = newExternalIPMapper(CandidateTypeUnspecified, nil) require.NoError(t, err, "should succeed") - require.Nil(t, m, "should be nil") + require.Nil(t, mapper, "should be nil") // ips being empty should succeed but mapper will still be nil - m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{}) + mapper, err = newExternalIPMapper(CandidateTypeUnspecified, []string{}) require.NoError(t, err, "should succeed") - require.Nil(t, m, "should be nil") + require.Nil(t, mapper, "should be nil") // IPv4 with no explicit local IP, defaults to CandidateTypeHost - m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ + mapper, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.4", }) require.NoError(t, err, "should succeed") - require.NotNil(t, m, "should not be nil") - require.Equal(t, CandidateTypeHost, m.candidateType, "should match") - require.NotNil(t, m.ipv4Mapping.ipSole) - require.Nil(t, m.ipv6Mapping.ipSole) - require.Equal(t, 0, len(m.ipv4Mapping.ipMap), "should match") - require.Equal(t, 0, len(m.ipv6Mapping.ipMap), "should match") + require.NotNil(t, mapper, "should not be nil") + require.Equal(t, CandidateTypeHost, mapper.candidateType, "should match") + require.NotNil(t, mapper.ipv4Mapping.ipSole) + require.Nil(t, mapper.ipv6Mapping.ipSole) + require.Equal(t, 0, len(mapper.ipv4Mapping.ipMap), "should match") + require.Equal(t, 0, len(mapper.ipv6Mapping.ipMap), "should match") // IPv4 with no explicit local IP, using CandidateTypeServerReflexive - m, err = newExternalIPMapper(CandidateTypeServerReflexive, []string{ + mapper, err = newExternalIPMapper(CandidateTypeServerReflexive, []string{ "1.2.3.4", }) require.NoError(t, err, "should succeed") - require.NotNil(t, m, "should not be nil") - require.Equal(t, CandidateTypeServerReflexive, m.candidateType, "should match") - require.NotNil(t, m.ipv4Mapping.ipSole) - require.Nil(t, m.ipv6Mapping.ipSole) - require.Equal(t, 0, len(m.ipv4Mapping.ipMap), "should match") - require.Equal(t, 0, len(m.ipv6Mapping.ipMap), "should match") + require.NotNil(t, mapper, "should not be nil") + require.Equal(t, CandidateTypeServerReflexive, mapper.candidateType, "should match") + require.NotNil(t, mapper.ipv4Mapping.ipSole) + require.Nil(t, mapper.ipv6Mapping.ipSole) + require.Equal(t, 0, len(mapper.ipv4Mapping.ipMap), "should match") + require.Equal(t, 0, len(mapper.ipv6Mapping.ipMap), "should match") // IPv4 with no explicit local IP, defaults to CandidateTypeHost - m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ + mapper, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "2601:4567::5678", }) require.NoError(t, err, "should succeed") - require.NotNil(t, m, "should not be nil") - require.Equal(t, CandidateTypeHost, m.candidateType, "should match") - require.Nil(t, m.ipv4Mapping.ipSole) - require.NotNil(t, m.ipv6Mapping.ipSole) - require.Equal(t, 0, len(m.ipv4Mapping.ipMap), "should match") - require.Equal(t, 0, len(m.ipv6Mapping.ipMap), "should match") + require.NotNil(t, mapper, "should not be nil") + require.Equal(t, CandidateTypeHost, mapper.candidateType, "should match") + require.Nil(t, mapper.ipv4Mapping.ipSole) + require.NotNil(t, mapper.ipv6Mapping.ipSole) + require.Equal(t, 0, len(mapper.ipv4Mapping.ipMap), "should match") + require.Equal(t, 0, len(mapper.ipv6Mapping.ipMap), "should match") // IPv4 and IPv6 in the mix - m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ + mapper, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.4", "2601:4567::5678", }) require.NoError(t, err, "should succeed") - require.NotNil(t, m, "should not be nil") - require.Equal(t, CandidateTypeHost, m.candidateType, "should match") - require.NotNil(t, m.ipv4Mapping.ipSole) - require.NotNil(t, m.ipv6Mapping.ipSole) - require.Equal(t, 0, len(m.ipv4Mapping.ipMap), "should match") - require.Equal(t, 0, len(m.ipv6Mapping.ipMap), "should match") + require.NotNil(t, mapper, "should not be nil") + require.Equal(t, CandidateTypeHost, mapper.candidateType, "should match") + require.NotNil(t, mapper.ipv4Mapping.ipSole) + require.NotNil(t, mapper.ipv6Mapping.ipSole) + require.Equal(t, 0, len(mapper.ipv4Mapping.ipMap), "should match") + require.Equal(t, 0, len(mapper.ipv6Mapping.ipMap), "should match") // Unsupported candidate type - CandidateTypePeerReflexive - m, err = newExternalIPMapper(CandidateTypePeerReflexive, []string{ + mapper, err = newExternalIPMapper(CandidateTypePeerReflexive, []string{ "1.2.3.4", }) require.Error(t, err, "should fail") - require.Nil(t, m, "should be nil") + require.Nil(t, mapper, "should be nil") // Unsupported candidate type - CandidateTypeRelay - m, err = newExternalIPMapper(CandidateTypePeerReflexive, []string{ + mapper, err = newExternalIPMapper(CandidateTypePeerReflexive, []string{ "1.2.3.4", }) require.Error(t, err, "should fail") - require.Nil(t, m, "should be nil") + require.Nil(t, mapper, "should be nil") // Cannot duplicate mapping IPv4 family - m, err = newExternalIPMapper(CandidateTypeServerReflexive, []string{ + mapper, err = newExternalIPMapper(CandidateTypeServerReflexive, []string{ "1.2.3.4", "5.6.7.8", }) require.Error(t, err, "should fail") - require.Nil(t, m, "should be nil") + require.Nil(t, mapper, "should be nil") // Cannot duplicate mapping IPv6 family - m, err = newExternalIPMapper(CandidateTypeServerReflexive, []string{ + mapper, err = newExternalIPMapper(CandidateTypeServerReflexive, []string{ "2201::1", "2201::0002", }) require.Error(t, err, "should fail") - require.Nil(t, m, "should be nil") + require.Nil(t, mapper, "should be nil") // Invalide external IP string - m, err = newExternalIPMapper(CandidateTypeServerReflexive, []string{ + mapper, err = newExternalIPMapper(CandidateTypeServerReflexive, []string{ "bad.2.3.4", }) require.Error(t, err, "should fail") - require.Nil(t, m, "should be nil") + require.Nil(t, mapper, "should be nil") // Invalide local IP string - m, err = newExternalIPMapper(CandidateTypeServerReflexive, []string{ + mapper, err = newExternalIPMapper(CandidateTypeServerReflexive, []string{ "1.2.3.4/10.0.0.bad", }) require.Error(t, err, "should fail") - require.Nil(t, m, "should be nil") + require.Nil(t, mapper, "should be nil") }) t.Run("newExternalIPMapper with explicit local IP", func(t *testing.T) { - var m *externalIPMapper + var mapper *externalIPMapper var err error // IPv4 with explicit local IP, defaults to CandidateTypeHost - m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ + mapper, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.4/10.0.0.1", }) require.NoError(t, err, "should succeed") - require.NotNil(t, m, "should not be nil") - require.Equal(t, CandidateTypeHost, m.candidateType, "should match") - require.Nil(t, m.ipv4Mapping.ipSole) - require.Nil(t, m.ipv6Mapping.ipSole) - require.Equal(t, 1, len(m.ipv4Mapping.ipMap), "should match") - require.Equal(t, 0, len(m.ipv6Mapping.ipMap), "should match") + require.NotNil(t, mapper, "should not be nil") + require.Equal(t, CandidateTypeHost, mapper.candidateType, "should match") + require.Nil(t, mapper.ipv4Mapping.ipSole) + require.Nil(t, mapper.ipv6Mapping.ipSole) + require.Equal(t, 1, len(mapper.ipv4Mapping.ipMap), "should match") + require.Equal(t, 0, len(mapper.ipv6Mapping.ipMap), "should match") // Cannot assign two ext IPs for one local IPv4 - m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ + mapper, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.4/10.0.0.1", "1.2.3.5/10.0.0.1", }) require.Error(t, err, "should fail") - require.Nil(t, m, "should be nil") + require.Nil(t, mapper, "should be nil") // Cannot assign two ext IPs for one local IPv6 - m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ + mapper, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "2200::1/fe80::1", "2200::0002/fe80::1", }) require.Error(t, err, "should fail") - require.Nil(t, m, "should be nil") + require.Nil(t, mapper, "should be nil") // Cannot mix different IP family in a pair (1) - m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ + mapper, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "2200::1/10.0.0.1", }) require.Error(t, err, "should fail") - require.Nil(t, m, "should be nil") + require.Nil(t, mapper, "should be nil") // Cannot mix different IP family in a pair (2) - m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ + mapper, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.4/fe80::1", }) require.Error(t, err, "should fail") - require.Nil(t, m, "should be nil") + require.Nil(t, mapper, "should be nil") // Invalid pair - m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ + mapper, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.4/192.168.0.2/10.0.0.1", }) require.Error(t, err, "should fail") - require.Nil(t, m, "should be nil") + require.Nil(t, mapper, "should be nil") }) t.Run("newExternalIPMapper with implicit and explicit local IP", func(t *testing.T) { @@ -209,100 +209,100 @@ func TestExternalIPMapper(t *testing.T) { }) t.Run("findExternalIP without explicit local IP", func(t *testing.T) { - var m *externalIPMapper + var mapper *externalIPMapper var err error var extIP net.IP // IPv4 with explicit local IP, defaults to CandidateTypeHost - m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ + mapper, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.4", "2200::1", }) require.NoError(t, err, "should succeed") - require.NotNil(t, m, "should not be nil") - require.NotNil(t, m.ipv4Mapping.ipSole) - require.NotNil(t, m.ipv6Mapping.ipSole) + require.NotNil(t, mapper, "should not be nil") + require.NotNil(t, mapper.ipv4Mapping.ipSole) + require.NotNil(t, mapper.ipv6Mapping.ipSole) // Find external IPv4 - extIP, err = m.findExternalIP("10.0.0.1") + extIP, err = mapper.findExternalIP("10.0.0.1") require.NoError(t, err, "should succeed") require.Equal(t, "1.2.3.4", extIP.String(), "should match") // Find external IPv6 - extIP, err = m.findExternalIP("fe80::0001") // Use '0001' instead of '1' on purpose + extIP, err = mapper.findExternalIP("fe80::0001") // Use '0001' instead of '1' on purpose require.NoError(t, err, "should succeed") require.Equal(t, "2200::1", extIP.String(), "should match") // Bad local IP string - _, err = m.findExternalIP("really.bad") + _, err = mapper.findExternalIP("really.bad") require.Error(t, err, "should fail") }) t.Run("findExternalIP with explicit local IP", func(t *testing.T) { - var m *externalIPMapper + var mapper *externalIPMapper var err error var extIP net.IP // IPv4 with explicit local IP, defaults to CandidateTypeHost - m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ + mapper, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.4/10.0.0.1", "1.2.3.5/10.0.0.2", "2200::1/fe80::1", "2200::2/fe80::2", }) require.NoError(t, err, "should succeed") - require.NotNil(t, m, "should not be nil") + require.NotNil(t, mapper, "should not be nil") // Find external IPv4 - extIP, err = m.findExternalIP("10.0.0.1") + extIP, err = mapper.findExternalIP("10.0.0.1") require.NoError(t, err, "should succeed") require.Equal(t, "1.2.3.4", extIP.String(), "should match") - extIP, err = m.findExternalIP("10.0.0.2") + extIP, err = mapper.findExternalIP("10.0.0.2") require.NoError(t, err, "should succeed") require.Equal(t, "1.2.3.5", extIP.String(), "should match") - _, err = m.findExternalIP("10.0.0.3") + _, err = mapper.findExternalIP("10.0.0.3") require.Error(t, err, "should fail") // Find external IPv6 - extIP, err = m.findExternalIP("fe80::0001") // Use '0001' instead of '1' on purpose + extIP, err = mapper.findExternalIP("fe80::0001") // Use '0001' instead of '1' on purpose require.NoError(t, err, "should succeed") require.Equal(t, "2200::1", extIP.String(), "should match") - extIP, err = m.findExternalIP("fe80::0002") // Use '0002' instead of '2' on purpose + extIP, err = mapper.findExternalIP("fe80::0002") // Use '0002' instead of '2' on purpose require.NoError(t, err, "should succeed") require.Equal(t, "2200::2", extIP.String(), "should match") - _, err = m.findExternalIP("fe80::3") + _, err = mapper.findExternalIP("fe80::3") require.Error(t, err, "should fail") // Bad local IP string - _, err = m.findExternalIP("really.bad") + _, err = mapper.findExternalIP("really.bad") require.Error(t, err, "should fail") }) t.Run("findExternalIP with empty map", func(t *testing.T) { - var m *externalIPMapper + var mapper *externalIPMapper var err error - m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ + mapper, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "1.2.3.4", }) require.NoError(t, err, "should succeed") // Attempt to find IPv6 that does not exist in the map - extIP, err := m.findExternalIP("fe80::1") + extIP, err := mapper.findExternalIP("fe80::1") require.NoError(t, err, "should succeed") require.Equal(t, "fe80::1", extIP.String(), "should match") - m, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ + mapper, err = newExternalIPMapper(CandidateTypeUnspecified, []string{ "2200::1", }) require.NoError(t, err, "should succeed") // Attempt to find IPv4 that does not exist in the map - extIP, err = m.findExternalIP("10.0.0.1") + extIP, err = mapper.findExternalIP("10.0.0.1") require.NoError(t, err, "should succeed") require.Equal(t, "10.0.0.1", extIP.String(), "should match") }) diff --git a/gather.go b/gather.go index 99b5c06..9d6bda0 100644 --- a/gather.go +++ b/gather.go @@ -21,10 +21,11 @@ import ( "github.com/pion/turn/v4" ) -// Close a net.Conn and log if we have a failure +// Close a net.Conn and log if we have a failure. func closeConnAndLog(c io.Closer, log logging.LeveledLogger, msg string, args ...interface{}) { if c == nil || (reflect.ValueOf(c).Kind() == reflect.Ptr && reflect.ValueOf(c).IsNil()) { log.Warnf("Connection is not allocated: "+msg, args...) + return } @@ -41,9 +42,11 @@ func (a *Agent) GatherCandidates() error { if runErr := a.loop.Run(a.loop, func(ctx context.Context) { if a.gatheringState != GatheringStateNew { gatherErr = ErrMultipleGatherAttempted + return } else if a.onCandidateHdlr.Load() == nil { gatherErr = ErrNoOnCandidateHandler + return } @@ -57,13 +60,15 @@ func (a *Agent) GatherCandidates() error { }); runErr != nil { return runErr } + return gatherErr } -func (a *Agent) gatherCandidates(ctx context.Context, done chan struct{}) { +func (a *Agent) gatherCandidates(ctx context.Context, done chan struct{}) { //nolint:cyclop defer close(done) if err := a.setGatheringState(GatheringStateGathering); err != nil { //nolint:contextcheck a.log.Warnf("Failed to set gatheringState to GatheringStateGathering: %v", err) + return } @@ -111,7 +116,8 @@ func (a *Agent) gatherCandidates(ctx context.Context, done chan struct{}) { } } -func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []NetworkType) { //nolint:gocognit +//nolint:gocognit,gocyclo,cyclop +func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []NetworkType) { networks := map[string]struct{}{} for _, networkType := range networkTypes { if networkType.IsTCP() { @@ -132,16 +138,19 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ _, localAddrs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, networkTypes, a.includeLoopback) if err != nil { a.log.Warnf("Failed to iterate local interfaces, host candidates will not be gathered %s", err) + return } for _, addr := range localAddrs { mappedIP := addr - if a.mDNSMode != MulticastDNSModeQueryAndGather && a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeHost { + if a.mDNSMode != MulticastDNSModeQueryAndGather && + a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeHost { if _mappedIP, innerErr := a.extIPMapper.findExternalIP(addr.String()); innerErr == nil { conv, ok := netip.AddrFromSlice(_mappedIP) if !ok { a.log.Warnf("failed to convert mapped external IP to netip.Addr'%s'", addr.String()) + continue } // we'd rather have an IPv4-mapped IPv6 become IPv4 so that it is usable @@ -186,6 +195,7 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ muxConns, err = multi.GetAllConns(a.localUfrag, mappedIP.Is6(), addr.AsSlice()) if err != nil { a.log.Warnf("Failed to get all TCP connections by ufrag: %s %s %s", network, addr, a.localUfrag) + continue } } else { @@ -194,6 +204,7 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ conn, err := a.tcpMux.GetConnByUfrag(a.localUfrag, mappedIP.Is6(), addr.AsSlice()) if err != nil { a.log.Warnf("Failed to get TCP connections by ufrag: %s %s %s", network, addr, a.localUfrag) + continue } muxConns = []net.PacketConn{conn} @@ -222,6 +233,7 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ }) if err != nil { a.log.Warnf("Failed to listen %s %s", network, addr) + continue } @@ -229,6 +241,7 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ conns = append(conns, connAndPort{conn, udpConn.Port}) } else { a.log.Warnf("Failed to get port of UDPAddr from ListenUDPInPortRange: %s %s %s", network, addr, a.localUfrag) + continue } } @@ -245,21 +258,38 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ IsLocationTracked: isLocationTracked, } - c, err := NewCandidateHost(&hostConfig) + candidateHost, err := NewCandidateHost(&hostConfig) if err != nil { - closeConnAndLog(connAndPort.conn, a.log, "failed to create host candidate: %s %s %d: %v", network, mappedIP, connAndPort.port, err) + closeConnAndLog( + connAndPort.conn, + a.log, + "failed to create host candidate: %s %s %d: %v", + network, mappedIP, + connAndPort.port, + err, + ) + continue } if a.mDNSMode == MulticastDNSModeQueryAndGather { - if err = c.setIPAddr(addr); err != nil { - closeConnAndLog(connAndPort.conn, a.log, "failed to create host candidate: %s %s %d: %v", network, mappedIP, connAndPort.port, err) + if err = candidateHost.setIPAddr(addr); err != nil { + closeConnAndLog( + connAndPort.conn, + a.log, + "failed to create host candidate: %s %s %d: %v", + network, + mappedIP, + connAndPort.port, + err, + ) + continue } } - if err := a.addCandidate(ctx, c, connAndPort.conn); err != nil { - if closeErr := c.close(); closeErr != nil { + if err := a.addCandidate(ctx, candidateHost, connAndPort.conn); err != nil { + if closeErr := candidateHost.close(); closeErr != nil { a.log.Warnf("Failed to close candidate: %v", closeErr) } a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v", err) @@ -287,10 +317,11 @@ func shouldFilterLocationTracked(candidateIP net.IP) bool { if !ok { return false } + return shouldFilterLocationTrackedIP(addr) } -func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolint:gocognit +func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolint:gocognit,cyclop if a.udpMux == nil { return errUDPMuxDisabled } @@ -317,6 +348,7 @@ func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolin mappedIP, err := a.extIPMapper.findExternalIP(candidateIP.String()) if err != nil { a.log.Warnf("1:1 NAT mapping is enabled but no external IP is found for %s", candidateIP.String()) + continue } @@ -359,6 +391,7 @@ func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolin c, err := NewCandidateHost(&hostConfig) if err != nil { closeConnAndLog(conn, a.log, "failed to create host mux candidate: %s %d: %v", candidateIP, udpAddr.Port, err) + continue } @@ -368,6 +401,7 @@ func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolin } closeConnAndLog(conn, a.log, "failed to add candidate: %s %d: %v", candidateIP, udpAddr.Port, err) + continue } @@ -391,26 +425,37 @@ func (a *Agent) gatherCandidatesSrflxMapped(ctx context.Context, networkTypes [] go func() { defer wg.Done() - conn, err := listenUDPInPortRange(a.net, a.log, int(a.portMax), int(a.portMin), network, &net.UDPAddr{IP: nil, Port: 0}) + conn, err := listenUDPInPortRange( + a.net, + a.log, + int(a.portMax), + int(a.portMin), + network, + &net.UDPAddr{IP: nil, Port: 0}, + ) if err != nil { a.log.Warnf("Failed to listen %s: %v", network, err) + return } lAddr, ok := conn.LocalAddr().(*net.UDPAddr) if !ok { closeConnAndLog(conn, a.log, "1:1 NAT mapping is enabled but LocalAddr is not a UDPAddr") + return } mappedIP, err := a.extIPMapper.findExternalIP(lAddr.IP.String()) if err != nil { closeConnAndLog(conn, a.log, "1:1 NAT mapping is enabled but no external IP is found for %s", lAddr.IP.String()) + return } if shouldFilterLocationTracked(mappedIP) { closeConnAndLog(conn, a.log, "external IP is somehow filtered for location tracking reasons %s", mappedIP) + return } @@ -429,6 +474,7 @@ func (a *Agent) gatherCandidatesSrflxMapped(ctx context.Context, networkTypes [] mappedIP.String(), lAddr.Port, err) + return } @@ -442,7 +488,8 @@ func (a *Agent) gatherCandidatesSrflxMapped(ctx context.Context, networkTypes [] } } -func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.URI, networkTypes []NetworkType) { //nolint:gocognit +//nolint:gocognit,cyclop +func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.URI, networkTypes []NetworkType) { var wg sync.WaitGroup defer wg.Wait() @@ -456,6 +503,7 @@ func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.UR udpAddr, ok := listenAddr.(*net.UDPAddr) if !ok { a.log.Warn("Failed to cast udpMuxSrflx listen address to UDPAddr") + continue } wg.Add(1) @@ -466,23 +514,27 @@ func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.UR serverAddr, err := a.net.ResolveUDPAddr(network, hostPort) if err != nil { a.log.Debugf("Failed to resolve STUN host: %s %s: %v", network, hostPort, err) + return } if shouldFilterLocationTracked(serverAddr.IP) { a.log.Warnf("STUN host %s is somehow filtered for location tracking reasons", hostPort) + return } xorAddr, err := a.udpMuxSrflx.GetXORMappedAddr(serverAddr, a.stunGatherTimeout) if err != nil { a.log.Warnf("Failed get server reflexive address %s %s: %v", network, url, err) + return } conn, err := a.udpMuxSrflx.GetConnForURL(a.localUfrag, url.String(), localAddr) if err != nil { a.log.Warnf("Failed to find connection in UDPMuxSrflx %s %s: %v", network, url, err) + return } @@ -500,6 +552,7 @@ func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.UR c, err := NewCandidateServerReflexive(&srflxConfig) if err != nil { closeConnAndLog(conn, a.log, "failed to create server reflexive candidate: %s %s %d: %v", network, ip, port, err) + return } @@ -515,7 +568,8 @@ func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.UR } } -func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, networkTypes []NetworkType) { //nolint:gocognit +//nolint:cyclop,gocognit +func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, networkTypes []NetworkType) { var wg sync.WaitGroup defer wg.Wait() @@ -533,17 +587,27 @@ func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, net serverAddr, err := a.net.ResolveUDPAddr(network, hostPort) if err != nil { a.log.Debugf("Failed to resolve STUN host: %s %s: %v", network, hostPort, err) + return } if shouldFilterLocationTracked(serverAddr.IP) { a.log.Warnf("STUN host %s is somehow filtered for location tracking reasons", hostPort) + return } - conn, err := listenUDPInPortRange(a.net, a.log, int(a.portMax), int(a.portMin), network, &net.UDPAddr{IP: nil, Port: 0}) + conn, err := listenUDPInPortRange( + a.net, + a.log, + int(a.portMax), + int(a.portMin), + network, + &net.UDPAddr{IP: nil, Port: 0}, + ) if err != nil { closeConnAndLog(conn, a.log, "failed to listen for %s: %v", serverAddr.String(), err) + return } // If the agent closes midway through the connection @@ -562,6 +626,7 @@ func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, net xorAddr, err := stunx.GetXORMappedAddr(conn, serverAddr, a.stunGatherTimeout) if err != nil { closeConnAndLog(conn, a.log, "failed to get server reflexive address %s %s: %v", network, url, err) + return } @@ -580,6 +645,7 @@ func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, net c, err := NewCandidateServerReflexive(&srflxConfig) if err != nil { closeConnAndLog(conn, a.log, "failed to create server reflexive candidate: %s %s %d: %v", network, ip, port, err) + return } @@ -594,7 +660,8 @@ func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, net } } -func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { //nolint:gocognit +//nolint:maintidx,gocognit,gocyclo,cyclop +func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { var wg sync.WaitGroup defer wg.Wait() @@ -605,9 +672,11 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / continue case urls[i].Username == "": a.log.Errorf("Failed to gather relay candidates: %v", ErrUsernameEmpty) + return case urls[i].Password == "": a.log.Errorf("Failed to gather relay candidates: %v", ErrPasswordEmpty) + return } @@ -627,6 +696,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / case url.Proto == stun.ProtoTypeUDP && url.Scheme == stun.SchemeTypeTURN: if locConn, err = a.net.ListenPacket(network, "0.0.0.0:0"); err != nil { a.log.Warnf("Failed to listen %s: %v", network, err) + return } @@ -638,6 +708,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / conn, connectErr := a.proxyDialer.Dial(NetworkTypeTCP4.String(), turnServerAddr) if connectErr != nil { a.log.Warnf("Failed to dial TCP address %s via proxy dialer: %v", turnServerAddr, connectErr) + return } @@ -654,12 +725,14 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / tcpAddr, connectErr := a.net.ResolveTCPAddr(NetworkTypeTCP4.String(), turnServerAddr) if connectErr != nil { a.log.Warnf("Failed to resolve TCP address %s: %v", turnServerAddr, connectErr) + return } conn, connectErr := a.net.DialTCP(NetworkTypeTCP4.String(), nil, tcpAddr) if connectErr != nil { a.log.Warnf("Failed to dial TCP address %s: %v", turnServerAddr, connectErr) + return } @@ -671,12 +744,14 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / udpAddr, connectErr := a.net.ResolveUDPAddr(network, turnServerAddr) if connectErr != nil { a.log.Warnf("Failed to resolve UDP address %s: %v", turnServerAddr, connectErr) + return } udpConn, dialErr := a.net.DialUDP("udp", nil, udpAddr) if dialErr != nil { a.log.Warnf("Failed to dial DTLS address %s: %v", turnServerAddr, dialErr) + return } @@ -686,11 +761,13 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / }) if connectErr != nil { a.log.Warnf("Failed to create DTLS client: %v", turnServerAddr, connectErr) + return } if connectErr = conn.HandshakeContext(ctx); connectErr != nil { a.log.Warnf("Failed to create DTLS client: %v", turnServerAddr, connectErr) + return } @@ -702,12 +779,14 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / tcpAddr, resolvErr := a.net.ResolveTCPAddr(NetworkTypeTCP4.String(), turnServerAddr) if resolvErr != nil { a.log.Warnf("Failed to resolve relay address %s: %v", turnServerAddr, resolvErr) + return } tcpConn, dialErr := a.net.DialTCP(NetworkTypeTCP4.String(), nil, tcpAddr) if dialErr != nil { a.log.Warnf("Failed to connect to relay: %v", dialErr) + return } @@ -721,6 +800,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / a.log.Errorf("Failed to close relay connection: %v", closeErr) } a.log.Warnf("Failed to connect to relay: %v", hsErr) + return } @@ -730,6 +810,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / locConn = turn.NewSTUNConn(conn) default: a.log.Warnf("Unable to handle URL in gatherCandidatesRelay %v", url) + return } @@ -743,12 +824,14 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / }) if err != nil { closeConnAndLog(locConn, a.log, "failed to create new TURN client %s %s", turnServerAddr, err) + return } if err = client.Listen(); err != nil { client.Close() closeConnAndLog(locConn, a.log, "failed to listen on TURN client %s %s", turnServerAddr, err) + return } @@ -756,6 +839,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / if err != nil { client.Close() closeConnAndLog(locConn, a.log, "failed to allocate on TURN client %s %s", turnServerAddr, err) + return } @@ -763,6 +847,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / if shouldFilterLocationTracked(rAddr.IP) { a.log.Warnf("TURN address %s is somehow filtered for location tracking reasons", rAddr.IP) + return } @@ -776,6 +861,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / RelayProtocol: relayProtocol, OnClose: func() error { client.Close() + return locConn.Close() }, } @@ -790,6 +876,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { / client.Close() closeConnAndLog(locConn, a.log, "failed to create relay candidate: %s %s: %v", network, rAddr.String(), err) + return } diff --git a/gather_test.go b/gather_test.go index 802f637..d450187 100644 --- a/gather_test.go +++ b/gather_test.go @@ -31,26 +31,32 @@ import ( ) func TestListenUDP(t *testing.T) { - a, err := NewAgent(&AgentConfig{}) + agent, err := NewAgent(&AgentConfig{}) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() - _, localAddrs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) + _, localAddrs, err := localInterfaces( + agent.net, + agent.interfaceFilter, + agent.ipFilter, + []NetworkType{NetworkTypeUDP4}, + false, + ) require.NotEqual(t, len(localAddrs), 0, "localInterfaces found no interfaces, unable to test") require.NoError(t, err) ip := localAddrs[0].AsSlice() - conn, err := listenUDPInPortRange(a.net, a.log, 0, 0, udp, &net.UDPAddr{IP: ip, Port: 0}) + conn, err := listenUDPInPortRange(agent.net, agent.log, 0, 0, udp, &net.UDPAddr{IP: ip, Port: 0}) require.NoError(t, err, "listenUDP error with no port restriction") require.NotNil(t, conn, "listenUDP error with no port restriction return a nil conn") - _, err = listenUDPInPortRange(a.net, a.log, 4999, 5000, udp, &net.UDPAddr{IP: ip, Port: 0}) + _, err = listenUDPInPortRange(agent.net, agent.log, 4999, 5000, udp, &net.UDPAddr{IP: ip, Port: 0}) require.Equal(t, err, ErrPort, "listenUDP with invalid port range did not return ErrPort") - conn, err = listenUDPInPortRange(a.net, a.log, 5000, 5000, udp, &net.UDPAddr{IP: ip, Port: 0}) + conn, err = listenUDPInPortRange(agent.net, agent.log, 5000, 5000, udp, &net.UDPAddr{IP: ip, Port: 0}) require.NoError(t, err, "listenUDP error with no port restriction") require.NotNil(t, conn, "listenUDP error with no port restriction return a nil conn") @@ -64,7 +70,7 @@ func TestListenUDP(t *testing.T) { result := make([]int, 0, total) portRange := make([]int, 0, total) for i := 0; i < total; i++ { - conn, err = listenUDPInPortRange(a.net, a.log, portMax, portMin, udp, &net.UDPAddr{IP: ip, Port: 0}) + conn, err = listenUDPInPortRange(agent.net, agent.log, portMax, portMin, udp, &net.UDPAddr{IP: ip, Port: 0}) require.NoError(t, err, "listenUDP error with no port restriction") require.NotNil(t, conn, "listenUDP error with no port restriction return a nil conn") @@ -85,7 +91,7 @@ func TestListenUDP(t *testing.T) { if !reflect.DeepEqual(result, portRange) { t.Fatalf("listenUDP with port restriction [%d, %d], got:%v, want:%v", portMin, portMax, result, portRange) } - _, err = listenUDPInPortRange(a.net, a.log, portMax, portMin, udp, &net.UDPAddr{IP: ip, Port: 0}) + _, err = listenUDPInPortRange(agent.net, agent.log, portMax, portMin, udp, &net.UDPAddr{IP: ip, Port: 0}) require.Equal(t, err, ErrPort, "listenUDP with port restriction [%d, %d], did not return ErrPort", portMin, portMax) } @@ -94,23 +100,23 @@ func TestGatherConcurrency(t *testing.T) { defer test.TimeOut(time.Second * 30).Stop() - a, err := NewAgent(&AgentConfig{ + agent, err := NewAgent(&AgentConfig{ NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}, IncludeLoopback: true, }) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) - require.NoError(t, a.OnCandidate(func(Candidate) { + require.NoError(t, agent.OnCandidate(func(Candidate) { candidateGatheredFunc() })) // Testing for panic for i := 0; i < 10; i++ { - _ = a.GatherCandidates() + _ = agent.GatherCandidates() } <-candidateGathered.Done() @@ -194,26 +200,27 @@ func TestLoopbackCandidate(t *testing.T) { for _, tc := range testCases { tcase := tc t.Run(tcase.name, func(t *testing.T) { - a, err := NewAgent(tc.agentConfig) + agent, err := NewAgent(tc.agentConfig) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) var loopback int32 - require.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, agent.OnCandidate(func(c Candidate) { if c != nil { if net.ParseIP(c.Address()).IsLoopback() { atomic.StoreInt32(&loopback, 1) } } else { candidateGatheredFunc() + return } t.Log(c.NetworkType(), c.Priority(), c) })) - require.NoError(t, a.GatherCandidates()) + require.NoError(t, agent.GatherCandidates()) <-candidateGathered.Done() @@ -226,7 +233,7 @@ func TestLoopbackCandidate(t *testing.T) { require.NoError(t, muxUnspecDefault.Close()) } -// Assert that STUN gathering is done concurrently +// Assert that STUN gathering is done concurrently. func TestSTUNConcurrency(t *testing.T) { defer test.CheckRoutines(t)() @@ -273,7 +280,7 @@ func TestSTUNConcurrency(t *testing.T) { _ = listener.Close() }() - a, err := NewAgent(&AgentConfig{ + agent, err := NewAgent(&AgentConfig{ NetworkTypes: supportedNetworkTypes(), Urls: urls, CandidateTypes: []CandidateType{CandidateTypeHost, CandidateTypeServerReflexive}, @@ -287,29 +294,36 @@ func TestSTUNConcurrency(t *testing.T) { }) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) - require.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, agent.OnCandidate(func(c Candidate) { if c == nil { candidateGatheredFunc() + return } t.Log(c.NetworkType(), c.Priority(), c) })) - require.NoError(t, a.GatherCandidates()) + require.NoError(t, agent.GatherCandidates()) <-candidateGathered.Done() } -// Assert that TURN gathering is done concurrently +// Assert that TURN gathering is done concurrently. func TestTURNConcurrency(t *testing.T) { defer test.CheckRoutines(t)() defer test.TimeOut(time.Second * 30).Stop() - runTest := func(protocol stun.ProtoType, scheme stun.SchemeType, packetConn net.PacketConn, listener net.Listener, serverPort int) { + runTest := func( + protocol stun.ProtoType, + scheme stun.SchemeType, + packetConn net.PacketConn, + listener net.Listener, + serverPort int, + ) { packetConnConfigs := []turn.PacketConnConfig{} if packetConn != nil { packetConnConfigs = append(packetConnConfigs, turn.PacketConnConfig{ @@ -357,7 +371,7 @@ func TestTURNConcurrency(t *testing.T) { Port: serverPort, }) - a, err := NewAgent(&AgentConfig{ + agent, err := NewAgent(&AgentConfig{ CandidateTypes: []CandidateType{CandidateTypeRelay}, InsecureSkipVerify: true, NetworkTypes: supportedNetworkTypes(), @@ -365,16 +379,16 @@ func TestTURNConcurrency(t *testing.T) { }) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) - require.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, agent.OnCandidate(func(c Candidate) { if c != nil { candidateGatheredFunc() } })) - require.NoError(t, a.GatherCandidates()) + require.NoError(t, agent.GatherCandidates()) <-candidateGathered.Done() } @@ -413,16 +427,20 @@ func TestTURNConcurrency(t *testing.T) { require.NoError(t, genErr) serverPort := randomPort(t) - serverListener, err := dtls.Listen("udp", &net.UDPAddr{IP: net.ParseIP(localhostIPStr), Port: serverPort}, &dtls.Config{ - Certificates: []tls.Certificate{certificate}, - }) + serverListener, err := dtls.Listen( + "udp", + &net.UDPAddr{IP: net.ParseIP(localhostIPStr), Port: serverPort}, + &dtls.Config{ + Certificates: []tls.Certificate{certificate}, + }, + ) require.NoError(t, err) runTest(stun.ProtoTypeUDP, stun.SchemeTypeTURNS, nil, serverListener, serverPort) }) } -// Assert that STUN and TURN gathering are done concurrently +// Assert that STUN and TURN gathering are done concurrently. func TestSTUNTURNConcurrency(t *testing.T) { defer test.CheckRoutines(t)() @@ -464,25 +482,26 @@ func TestSTUNTURNConcurrency(t *testing.T) { Password: "password", }) - a, err := NewAgent(&AgentConfig{ + agent, err := NewAgent(&AgentConfig{ NetworkTypes: supportedNetworkTypes(), Urls: urls, CandidateTypes: []CandidateType{CandidateTypeServerReflexive, CandidateTypeRelay}, }) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() { - gatherLim := test.TimeOut(time.Second * 3) // As TURN and STUN should be checked in parallel, this should complete before the default STUN timeout (5s) + // As TURN and STUN should be checked in parallel, this should complete before the default STUN timeout (5s) + gatherLim := test.TimeOut(time.Second * 3) candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) - require.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, agent.OnCandidate(func(c Candidate) { if c != nil { candidateGatheredFunc() } })) - require.NoError(t, a.GatherCandidates()) + require.NoError(t, agent.GatherCandidates()) <-candidateGathered.Done() gatherLim.Stop() @@ -528,24 +547,24 @@ func TestTURNSrflx(t *testing.T) { Password: "password", }} - a, err := NewAgent(&AgentConfig{ + agent, err := NewAgent(&AgentConfig{ NetworkTypes: supportedNetworkTypes(), Urls: urls, CandidateTypes: []CandidateType{CandidateTypeServerReflexive, CandidateTypeRelay}, }) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) - require.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, agent.OnCandidate(func(c Candidate) { if c != nil && c.Type() == CandidateTypeServerReflexive { candidateGatheredFunc() } })) - require.NoError(t, a.GatherCandidates()) + require.NoError(t, agent.GatherCandidates()) <-candidateGathered.Done() } @@ -580,6 +599,7 @@ func (m *mockConn) SetWriteDeadline(time.Time) error { return io.EOF } func (m *mockProxy) Dial(string, string) (net.Conn, error) { m.proxyWasDialed() + return &mockConn{}, nil } @@ -599,7 +619,7 @@ func TestTURNProxyDialer(t *testing.T) { proxyDialer, err := proxy.FromURL(tcpProxyURI, proxy.Direct) require.NoError(t, err) - a, err := NewAgent(&AgentConfig{ + agent, err := NewAgent(&AgentConfig{ CandidateTypes: []CandidateType{CandidateTypeRelay}, NetworkTypes: supportedNetworkTypes(), Urls: []*stun.URI{ @@ -616,17 +636,17 @@ func TestTURNProxyDialer(t *testing.T) { }) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() candidateGatherFinish, candidateGatherFinishFunc := context.WithCancel(context.Background()) - require.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, agent.OnCandidate(func(c Candidate) { if c == nil { candidateGatherFinishFunc() } })) - require.NoError(t, a.GatherCandidates()) + require.NoError(t, agent.GatherCandidates()) <-candidateGatherFinish.Done() <-proxyWasDialed.Done() } @@ -651,31 +671,31 @@ func TestUDPMuxDefaultWithNAT1To1IPsUsage(t *testing.T) { _ = mux.Close() }() - a, err := NewAgent(&AgentConfig{ + agent, err := NewAgent(&AgentConfig{ NAT1To1IPs: []string{"1.2.3.4"}, NAT1To1IPCandidateType: CandidateTypeHost, UDPMux: mux, }) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() gatherCandidateDone := make(chan struct{}) - require.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, agent.OnCandidate(func(c Candidate) { if c == nil { close(gatherCandidateDone) } else { require.Equal(t, "1.2.3.4", c.Address()) } })) - require.NoError(t, a.GatherCandidates()) + require.NoError(t, agent.GatherCandidates()) <-gatherCandidateDone require.NotEqual(t, 0, len(mux.connsIPv4)) } -// Assert that candidates are given for each mux in a MultiUDPMux +// Assert that candidates are given for each mux in a MultiUDPMux. func TestMultiUDPMuxUsage(t *testing.T) { defer test.CheckRoutines(t)() @@ -700,25 +720,26 @@ func TestMultiUDPMuxUsage(t *testing.T) { }() } - a, err := NewAgent(&AgentConfig{ + agent, err := NewAgent(&AgentConfig{ NetworkTypes: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}, CandidateTypes: []CandidateType{CandidateTypeHost}, UDPMux: NewMultiUDPMuxDefault(udpMuxInstances...), }) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() candidateCh := make(chan Candidate) - require.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, agent.OnCandidate(func(c Candidate) { if c == nil { close(candidateCh) + return } candidateCh <- c })) - require.NoError(t, a.GatherCandidates()) + require.NoError(t, agent.GatherCandidates()) portFound := make(map[int]bool) for c := range candidateCh { @@ -731,7 +752,7 @@ func TestMultiUDPMuxUsage(t *testing.T) { } } -// Assert that candidates are given for each mux in a MultiTCPMux +// Assert that candidates are given for each mux in a MultiTCPMux. func TestMultiTCPMuxUsage(t *testing.T) { defer test.CheckRoutines(t)() @@ -757,25 +778,26 @@ func TestMultiTCPMuxUsage(t *testing.T) { })) } - a, err := NewAgent(&AgentConfig{ + agent, err := NewAgent(&AgentConfig{ NetworkTypes: supportedNetworkTypes(), CandidateTypes: []CandidateType{CandidateTypeHost}, TCPMux: NewMultiTCPMuxDefault(tcpMuxInstances...), }) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() candidateCh := make(chan Candidate) - require.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, agent.OnCandidate(func(c Candidate) { if c == nil { close(candidateCh) + return } candidateCh <- c })) - require.NoError(t, a.GatherCandidates()) + require.NoError(t, agent.GatherCandidates()) portFound := make(map[int]bool) for c := range candidateCh { @@ -790,7 +812,7 @@ func TestMultiTCPMuxUsage(t *testing.T) { } } -// Assert that UniversalUDPMux is used while gathering when configured in the Agent +// Assert that UniversalUDPMux is used while gathering when configured in the Agent. func TestUniversalUDPMuxUsage(t *testing.T) { defer test.CheckRoutines(t)() @@ -816,7 +838,7 @@ func TestUniversalUDPMuxUsage(t *testing.T) { }) } - a, err := NewAgent(&AgentConfig{ + agent, err := NewAgent(&AgentConfig{ NetworkTypes: supportedNetworkTypes(), Urls: urls, CandidateTypes: []CandidateType{CandidateTypeServerReflexive}, @@ -828,26 +850,32 @@ func TestUniversalUDPMuxUsage(t *testing.T) { if aClosed { return } - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() candidateGathered, candidateGatheredFunc := context.WithCancel(context.Background()) - require.NoError(t, a.OnCandidate(func(c Candidate) { + require.NoError(t, agent.OnCandidate(func(c Candidate) { if c == nil { candidateGatheredFunc() + return } t.Log(c.NetworkType(), c.Priority(), c) })) - require.NoError(t, a.GatherCandidates()) + require.NoError(t, agent.GatherCandidates()) <-candidateGathered.Done() - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) aClosed = true // Twice because of 2 STUN servers configured - require.Equal(t, numSTUNS, udpMuxSrflx.getXORMappedAddrUsedTimes, "expected times that GetXORMappedAddr should be called") + require.Equal( + t, + numSTUNS, + udpMuxSrflx.getXORMappedAddrUsedTimes, + "expected times that GetXORMappedAddr should be called", + ) // One for Restart() when agent has been initialized and one time when Close() the agent require.Equal(t, 2, udpMuxSrflx.removeConnByUfragTimes, "expected times that RemoveConnByUfrag should be called") // Twice because of 2 STUN servers configured @@ -871,6 +899,7 @@ func (m *universalUDPMuxMock) GetConnForURL(string, string, net.Addr) (net.Packe m.mu.Lock() defer m.mu.Unlock() m.getConnForURLTimes++ + return m.conn, nil } @@ -878,6 +907,7 @@ func (m *universalUDPMuxMock) GetXORMappedAddr(net.Addr, time.Duration) (*stun.X m.mu.Lock() defer m.mu.Unlock() m.getXORMappedAddrUsedTimes++ + return &stun.XORMappedAddress{IP: net.IP{100, 64, 0, 1}, Port: 77878}, nil } diff --git a/gather_vnet_test.go b/gather_vnet_test.go index f8754d3..c5a3f1d 100644 --- a/gather_vnet_test.go +++ b/gather_vnet_test.go @@ -20,7 +20,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestVNetGather(t *testing.T) { +func TestVNetGather(t *testing.T) { //nolint:cyclop defer test.CheckRoutines(t)() loggerFactory := logging.NewDefaultLoggerFactory() @@ -51,7 +51,7 @@ func TestVNetGather(t *testing.T) { t.Fatalf("Failed to parse CIDR: %s", err) } - r, err := vnet.NewRouter(&vnet.RouterConfig{ + router, err := vnet.NewRouter(&vnet.RouterConfig{ CIDR: cider, LoggerFactory: loggerFactory, }) @@ -64,7 +64,7 @@ func TestVNetGather(t *testing.T) { t.Fatalf("Failed to create a Net: %s", err) } - err = r.AddNet(nw) + err = router.AddNet(nw) if err != nil { t.Fatalf("Failed to add a Net to the router: %s", err) } @@ -94,7 +94,7 @@ func TestVNetGather(t *testing.T) { }) t.Run("listenUDP", func(t *testing.T) { - r, err := vnet.NewRouter(&vnet.RouterConfig{ + router, err := vnet.NewRouter(&vnet.RouterConfig{ CIDR: "1.2.3.0/24", LoggerFactory: loggerFactory, }) @@ -107,20 +107,26 @@ func TestVNetGather(t *testing.T) { t.Fatalf("Failed to create a Net: %s", err) } - err = r.AddNet(nw) + err = router.AddNet(nw) if err != nil { t.Fatalf("Failed to add a Net to the router: %s", err) } - a, err := NewAgent(&AgentConfig{Net: nw}) + agent, err := NewAgent(&AgentConfig{Net: nw}) if err != nil { t.Fatalf("Failed to create agent: %s", err) } defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() - _, localAddrs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) + _, localAddrs, err := localInterfaces( + agent.net, + agent.interfaceFilter, + agent.ipFilter, + []NetworkType{NetworkTypeUDP4}, + false, + ) if len(localAddrs) == 0 { t.Fatal("localInterfaces found no interfaces, unable to test") } @@ -128,7 +134,7 @@ func TestVNetGather(t *testing.T) { ip := localAddrs[0].AsSlice() - conn, err := listenUDPInPortRange(a.net, a.log, 0, 0, udp, &net.UDPAddr{IP: ip, Port: 0}) + conn, err := listenUDPInPortRange(agent.net, agent.log, 0, 0, udp, &net.UDPAddr{IP: ip, Port: 0}) if err != nil { t.Fatalf("listenUDP error with no port restriction %v", err) } else if conn == nil { @@ -139,12 +145,12 @@ func TestVNetGather(t *testing.T) { t.Fatalf("failed to close conn") } - _, err = listenUDPInPortRange(a.net, a.log, 4999, 5000, udp, &net.UDPAddr{IP: ip, Port: 0}) + _, err = listenUDPInPortRange(agent.net, agent.log, 4999, 5000, udp, &net.UDPAddr{IP: ip, Port: 0}) if !errors.Is(err, ErrPort) { t.Fatal("listenUDP with invalid port range did not return ErrPort") } - conn, err = listenUDPInPortRange(a.net, a.log, 5000, 5000, udp, &net.UDPAddr{IP: ip, Port: 0}) + conn, err = listenUDPInPortRange(agent.net, agent.log, 5000, 5000, udp, &net.UDPAddr{IP: ip, Port: 0}) if err != nil { t.Fatalf("listenUDP error with no port restriction %v", err) } else if conn == nil { @@ -163,7 +169,7 @@ func TestVNetGather(t *testing.T) { }) } -func TestVNetGatherWithNAT1To1(t *testing.T) { +func TestVNetGatherWithNAT1To1(t *testing.T) { //nolint:cyclop defer test.CheckRoutines(t)() loggerFactory := logging.NewDefaultLoggerFactory() @@ -206,7 +212,7 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { err = lan.AddNet(nw) require.NoError(t, err, "should succeed") - a, err := NewAgent(&AgentConfig{ + agent, err := NewAgent(&AgentConfig{ NetworkTypes: []NetworkType{ NetworkTypeUDP4, }, @@ -215,25 +221,25 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { }) require.NoError(t, err, "should succeed") defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() done := make(chan struct{}) - err = a.OnCandidate(func(c Candidate) { + err = agent.OnCandidate(func(c Candidate) { if c == nil { close(done) } }) require.NoError(t, err, "should succeed") - err = a.GatherCandidates() + err = agent.GatherCandidates() require.NoError(t, err, "should succeed") log.Debug("Wait until gathering is complete...") <-done log.Debug("Gathering is done") - candidates, err := a.GetLocalCandidates() + candidates, err := agent.GetLocalCandidates() require.NoError(t, err, "should succeed") if len(candidates) != 2 { @@ -248,7 +254,7 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { } } - if candidates[0].Address() == externalIP0 { + if candidates[0].Address() == externalIP0 { //nolint:nestif if candidates[1].Address() != externalIP1 { t.Fatalf("Unexpected candidate IP: %s", candidates[1].Address()) } @@ -305,7 +311,7 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { err = lan.AddNet(nw) require.NoError(t, err, "should succeed") - a, err := NewAgent(&AgentConfig{ + agent, err := NewAgent(&AgentConfig{ NetworkTypes: []NetworkType{ NetworkTypeUDP4, }, @@ -317,25 +323,25 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { }) require.NoError(t, err, "should succeed") defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() done := make(chan struct{}) - err = a.OnCandidate(func(c Candidate) { + err = agent.OnCandidate(func(c Candidate) { if c == nil { close(done) } }) require.NoError(t, err, "should succeed") - err = a.GatherCandidates() + err = agent.GatherCandidates() require.NoError(t, err, "should succeed") log.Debug("Wait until gathering is complete...") <-done log.Debug("Gathering is done") - candidates, err := a.GetLocalCandidates() + candidates, err := agent.GetLocalCandidates() require.NoError(t, err, "should succeed") if len(candidates) != 2 { @@ -367,7 +373,7 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { defer test.CheckRoutines(t)() loggerFactory := logging.NewDefaultLoggerFactory() - r, err := vnet.NewRouter(&vnet.RouterConfig{ + router, err := vnet.NewRouter(&vnet.RouterConfig{ CIDR: "1.2.3.0/24", LoggerFactory: loggerFactory, }) @@ -380,24 +386,31 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { t.Fatalf("Failed to create a Net: %s", err) } - if err = r.AddNet(nw); err != nil { + if err = router.AddNet(nw); err != nil { t.Fatalf("Failed to add a Net to the router: %s", err) } t.Run("InterfaceFilter should exclude the interface", func(t *testing.T) { - a, err := NewAgent(&AgentConfig{ + agent, err := NewAgent(&AgentConfig{ Net: nw, InterfaceFilter: func(interfaceName string) (keep bool) { require.Equal(t, "eth0", interfaceName) + return false }, }) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() - _, localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) + _, localIPs, err := localInterfaces( + agent.net, + agent.interfaceFilter, + agent.ipFilter, + []NetworkType{NetworkTypeUDP4}, + false, + ) require.NoError(t, err) if len(localIPs) != 0 { @@ -406,19 +419,26 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { }) t.Run("IPFilter should exclude the IP", func(t *testing.T) { - a, err := NewAgent(&AgentConfig{ + agent, err := NewAgent(&AgentConfig{ Net: nw, IPFilter: func(ip net.IP) (keep bool) { require.Equal(t, net.IP{1, 2, 3, 1}, ip) + return false }, }) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() - _, localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) + _, localIPs, err := localInterfaces( + agent.net, + agent.interfaceFilter, + agent.ipFilter, + []NetworkType{NetworkTypeUDP4}, + false, + ) require.NoError(t, err) if len(localIPs) != 0 { @@ -427,19 +447,26 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { }) t.Run("InterfaceFilter should not exclude the interface", func(t *testing.T) { - a, err := NewAgent(&AgentConfig{ + agent, err := NewAgent(&AgentConfig{ Net: nw, InterfaceFilter: func(interfaceName string) (keep bool) { require.Equal(t, "eth0", interfaceName) + return true }, }) require.NoError(t, err) defer func() { - require.NoError(t, a.Close()) + require.NoError(t, agent.Close()) }() - _, localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) + _, localIPs, err := localInterfaces( + agent.net, + agent.interfaceFilter, + agent.ipFilter, + []NetworkType{NetworkTypeUDP4}, + false, + ) require.NoError(t, err) if len(localIPs) == 0 { diff --git a/ice.go b/ice.go index 73262dd..bd53702 100644 --- a/ice.go +++ b/ice.go @@ -3,33 +3,33 @@ package ice -// ConnectionState is an enum showing the state of a ICE Connection +// ConnectionState is an enum showing the state of a ICE Connection. type ConnectionState int -// List of supported States +// List of supported States. const ( - // ConnectionStateUnknown represents an unknown state + // ConnectionStateUnknown represents an unknown state. ConnectionStateUnknown ConnectionState = iota - // ConnectionStateNew ICE agent is gathering addresses + // ConnectionStateNew ICE agent is gathering addresses. ConnectionStateNew - // ConnectionStateChecking ICE agent has been given local and remote candidates, and is attempting to find a match + // ConnectionStateChecking ICE agent has been given local and remote candidates, and is attempting to find a match. ConnectionStateChecking - // ConnectionStateConnected ICE agent has a pairing, but is still checking other pairs + // ConnectionStateConnected ICE agent has a pairing, but is still checking other pairs. ConnectionStateConnected - // ConnectionStateCompleted ICE agent has finished + // ConnectionStateCompleted ICE agent has finished. ConnectionStateCompleted - // ConnectionStateFailed ICE agent never could successfully connect + // ConnectionStateFailed ICE agent never could successfully connect. ConnectionStateFailed - // ConnectionStateDisconnected ICE agent connected successfully, but has entered a failed state + // ConnectionStateDisconnected ICE agent connected successfully, but has entered a failed state. ConnectionStateDisconnected - // ConnectionStateClosed ICE agent has finished and is no longer handling requests + // ConnectionStateClosed ICE agent has finished and is no longer handling requests. ConnectionStateClosed ) @@ -54,20 +54,20 @@ func (c ConnectionState) String() string { } } -// GatheringState describes the state of the candidate gathering process +// GatheringState describes the state of the candidate gathering process. type GatheringState int const ( - // GatheringStateUnknown represents an unknown state + // GatheringStateUnknown represents an unknown state. GatheringStateUnknown GatheringState = iota - // GatheringStateNew indicates candidate gathering is not yet started + // GatheringStateNew indicates candidate gathering is not yet started. GatheringStateNew - // GatheringStateGathering indicates candidate gathering is ongoing + // GatheringStateGathering indicates candidate gathering is ongoing. GatheringStateGathering - // GatheringStateComplete indicates candidate gathering has been completed + // GatheringStateComplete indicates candidate gathering has been completed. GatheringStateComplete ) diff --git a/icecontrol.go b/icecontrol.go index 922f79a..fcf08a0 100644 --- a/icecontrol.go +++ b/icecontrol.go @@ -20,6 +20,7 @@ func (a tiebreaker) AddToAs(m *stun.Message, t stun.AttrType) error { v := make([]byte, tiebreakerSize) binary.BigEndian.PutUint64(v, uint64(a)) m.Add(t, v) + return nil } @@ -33,6 +34,7 @@ func (a *tiebreaker) GetFromAs(m *stun.Message, t stun.AttrType) error { return err } *a = tiebreaker(binary.BigEndian.Uint64(v)) + return nil } @@ -73,6 +75,7 @@ func (c AttrControl) AddTo(m *stun.Message) error { if c.Role == Controlling { return tiebreaker(c.Tiebreaker).AddToAs(m, stun.AttrICEControlling) } + return tiebreaker(c.Tiebreaker).AddToAs(m, stun.AttrICEControlled) } @@ -80,11 +83,14 @@ func (c AttrControl) AddTo(m *stun.Message) error { func (c *AttrControl) GetFrom(m *stun.Message) error { if m.Contains(stun.AttrICEControlling) { c.Role = Controlling + return (*tiebreaker)(&c.Tiebreaker).GetFromAs(m, stun.AttrICEControlling) } if m.Contains(stun.AttrICEControlled) { c.Role = Controlled + return (*tiebreaker)(&c.Tiebreaker).GetFromAs(m, stun.AttrICEControlled) } + return stun.ErrAttributeNotFound } diff --git a/icecontrol_test.go b/icecontrol_test.go index a0a57bc..8d710d9 100644 --- a/icecontrol_test.go +++ b/icecontrol_test.go @@ -12,11 +12,11 @@ import ( func TestControlled_GetFrom(t *testing.T) { //nolint:dupl m := new(stun.Message) - var c AttrControlled - if err := c.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) { + var attrCtr AttrControlled + if err := attrCtr.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) { t.Error("unexpected error") } - if err := m.Build(stun.BindingRequest, &c); err != nil { + if err := m.Build(stun.BindingRequest, &attrCtr); err != nil { t.Error(err) } m1 := new(stun.Message) @@ -27,7 +27,7 @@ func TestControlled_GetFrom(t *testing.T) { //nolint:dupl if err := c1.GetFrom(m1); err != nil { t.Error(err) } - if c1 != c { + if c1 != attrCtr { t.Error("not equal") } t.Run("IncorrectSize", func(t *testing.T) { @@ -42,11 +42,11 @@ func TestControlled_GetFrom(t *testing.T) { //nolint:dupl func TestControlling_GetFrom(t *testing.T) { //nolint:dupl m := new(stun.Message) - var c AttrControlling - if err := c.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) { + var attrCtr AttrControlling + if err := attrCtr.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) { t.Error("unexpected error") } - if err := m.Build(stun.BindingRequest, &c); err != nil { + if err := m.Build(stun.BindingRequest, &attrCtr); err != nil { t.Error(err) } m1 := new(stun.Message) @@ -57,7 +57,7 @@ func TestControlling_GetFrom(t *testing.T) { //nolint:dupl if err := c1.GetFrom(m1); err != nil { t.Error(err) } - if c1 != c { + if c1 != attrCtr { t.Error("not equal") } t.Run("IncorrectSize", func(t *testing.T) { @@ -70,7 +70,7 @@ func TestControlling_GetFrom(t *testing.T) { //nolint:dupl }) } -func TestControl_GetFrom(t *testing.T) { +func TestControl_GetFrom(t *testing.T) { //nolint:cyclop t.Run("Blank", func(t *testing.T) { m := new(stun.Message) var c AttrControl @@ -80,13 +80,13 @@ func TestControl_GetFrom(t *testing.T) { }) t.Run("Controlling", func(t *testing.T) { //nolint:dupl m := new(stun.Message) - var c AttrControl - if err := c.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) { + var attCtr AttrControl + if err := attCtr.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) { t.Error("unexpected error") } - c.Role = Controlling - c.Tiebreaker = 4321 - if err := m.Build(stun.BindingRequest, &c); err != nil { + attCtr.Role = Controlling + attCtr.Tiebreaker = 4321 + if err := m.Build(stun.BindingRequest, &attCtr); err != nil { t.Error(err) } m1 := new(stun.Message) @@ -97,7 +97,7 @@ func TestControl_GetFrom(t *testing.T) { if err := c1.GetFrom(m1); err != nil { t.Error(err) } - if c1 != c { + if c1 != attCtr { t.Error("not equal") } t.Run("IncorrectSize", func(t *testing.T) { @@ -111,13 +111,13 @@ func TestControl_GetFrom(t *testing.T) { }) t.Run("Controlled", func(t *testing.T) { //nolint:dupl m := new(stun.Message) - var c AttrControl - if err := c.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) { + var attrCtrl AttrControl + if err := attrCtrl.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) { t.Error("unexpected error") } - c.Role = Controlled - c.Tiebreaker = 1234 - if err := m.Build(stun.BindingRequest, &c); err != nil { + attrCtrl.Role = Controlled + attrCtrl.Tiebreaker = 1234 + if err := m.Build(stun.BindingRequest, &attrCtrl); err != nil { t.Error(err) } m1 := new(stun.Message) @@ -128,7 +128,7 @@ func TestControl_GetFrom(t *testing.T) { if err := c1.GetFrom(m1); err != nil { t.Error(err) } - if c1 != c { + if c1 != attrCtrl { t.Error("not equal") } t.Run("IncorrectSize", func(t *testing.T) { diff --git a/internal/atomic/atomic.go b/internal/atomic/atomic.go index f8caf5a..f170133 100644 --- a/internal/atomic/atomic.go +++ b/internal/atomic/atomic.go @@ -6,18 +6,19 @@ package atomic import "sync/atomic" -// Error is an atomic error +// Error is an atomic error. type Error struct { v atomic.Value } -// Store updates the value of the atomic variable +// Store updates the value of the atomic variable. func (a *Error) Store(err error) { a.v.Store(struct{ error }{err}) } -// Load retrieves the current value of the atomic variable +// Load retrieves the current value of the atomic variable. func (a *Error) Load() error { err, _ := a.v.Load().(struct{ error }) + return err.error } diff --git a/internal/fakenet/mock_conn.go b/internal/fakenet/mock_conn.go index cc98849..baf012a 100644 --- a/internal/fakenet/mock_conn.go +++ b/internal/fakenet/mock_conn.go @@ -11,7 +11,7 @@ import ( "time" ) -// MockPacketConn for tests +// MockPacketConn for tests. type MockPacketConn struct{} func (m *MockPacketConn) ReadFrom([]byte) (n int, addr net.Addr, err error) { return 0, nil, nil } //nolint:revive diff --git a/internal/fakenet/packet_conn.go b/internal/fakenet/packet_conn.go index 0b9faaa..f9cb66f 100644 --- a/internal/fakenet/packet_conn.go +++ b/internal/fakenet/packet_conn.go @@ -8,18 +8,19 @@ import ( "net" ) -// Compile-time assertion +// Compile-time assertion. var _ net.PacketConn = (*PacketConn)(nil) -// PacketConn wraps a net.Conn and emulates net.PacketConn +// PacketConn wraps a net.Conn and emulates net.PacketConn. type PacketConn struct { net.Conn } -// ReadFrom reads a packet from the connection, +// ReadFrom reads a packet from the connection. func (f *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { n, err = f.Conn.Read(p) addr = f.Conn.RemoteAddr() + return } diff --git a/internal/stun/stun.go b/internal/stun/stun.go index 60379eb..55ccb9b 100644 --- a/internal/stun/stun.go +++ b/internal/stun/stun.go @@ -59,7 +59,7 @@ func GetXORMappedAddr(conn net.PacketConn, serverAddr net.Addr, timeout time.Dur return &addr, nil } -// AssertUsername checks that the given STUN message m has a USERNAME attribute with a given value +// AssertUsername checks that the given STUN message m has a USERNAME attribute with a given value. func AssertUsername(m *stun.Message, expectedUsername string) error { var username stun.Username if err := username.GetFrom(m); err != nil { diff --git a/internal/taskloop/taskloop.go b/internal/taskloop/taskloop.go index d025998..15a2666 100644 --- a/internal/taskloop/taskloop.go +++ b/internal/taskloop/taskloop.go @@ -13,7 +13,7 @@ import ( atomicx "github.com/pion/ice/v4/internal/atomic" ) -// ErrClosed indicates that the loop has been stopped +// ErrClosed indicates that the loop has been stopped. var ErrClosed = errors.New("the agent is closed") type task struct { @@ -21,7 +21,7 @@ type task struct { done chan struct{} } -// Loop runs submitted task serially in a dedicated Goroutine +// Loop runs submitted task serially in a dedicated Goroutine. type Loop struct { tasks chan task @@ -31,7 +31,7 @@ type Loop struct { err atomicx.Error } -// New creates and starts a new task loop +// New creates and starts a new task loop. func New(onClose func()) *Loop { l := &Loop{ tasks: make(chan task), @@ -40,6 +40,7 @@ func New(onClose func()) *Loop { } go l.runLoop(onClose) + return l } @@ -86,6 +87,7 @@ func (l *Loop) Run(ctx context.Context, t func(context.Context)) error { return ctx.Err() case l.tasks <- task{t, done}: <-done + return nil } } @@ -113,7 +115,7 @@ func (l *Loop) Deadline() (deadline time.Time, ok bool) { return time.Time{}, false } -// Value is not supported for task loops +// Value is not supported for task loops. func (l *Loop) Value(interface{}) interface{} { return nil } diff --git a/mdns.go b/mdns.go index 2c10a32..ac0756d 100644 --- a/mdns.go +++ b/mdns.go @@ -14,18 +14,19 @@ import ( "golang.org/x/net/ipv6" ) -// MulticastDNSMode represents the different Multicast modes ICE can run in +// MulticastDNSMode represents the different Multicast modes ICE can run in. type MulticastDNSMode byte -// MulticastDNSMode enum +// MulticastDNSMode enum. const ( - // MulticastDNSModeDisabled means remote mDNS candidates will be discarded, and local host candidates will use IPs + // MulticastDNSModeDisabled means remote mDNS candidates will be discarded, and local host candidates will use IPs. MulticastDNSModeDisabled MulticastDNSMode = iota + 1 - // MulticastDNSModeQueryOnly means remote mDNS candidates will be accepted, and local host candidates will use IPs + // MulticastDNSModeQueryOnly means remote mDNS candidates will be accepted, and local host candidates will use IPs. MulticastDNSModeQueryOnly - // MulticastDNSModeQueryAndGather means remote mDNS candidates will be accepted, and local host candidates will use mDNS + // MulticastDNSModeQueryAndGather means remote mDNS candidates will be accepted, + // and local host candidates will use mDNS. MulticastDNSModeQueryAndGather ) @@ -33,11 +34,13 @@ func generateMulticastDNSName() (string, error) { // https://tools.ietf.org/id/draft-ietf-rtcweb-mdns-ice-candidates-02.html#gathering // The unique name MUST consist of a version 4 UUID as defined in [RFC4122], followed by “.local”. u, err := uuid.NewRandom() + return u.String() + ".local", err } +//nolint:cyclop func createMulticastDNS( - n transport.Net, + netTransport transport.Net, networkTypes []NetworkType, interfaces []*transport.Interface, includeLoopback bool, @@ -57,6 +60,7 @@ func createMulticastDNS( for _, nt := range networkTypes { if nt.IsIPv4() { useV4 = true + continue } if nt.IsIPv6() { @@ -65,11 +69,11 @@ func createMulticastDNS( } } - addr4, mdnsErr := n.ResolveUDPAddr("udp4", mdns.DefaultAddressIPv4) + addr4, mdnsErr := netTransport.ResolveUDPAddr("udp4", mdns.DefaultAddressIPv4) if mdnsErr != nil { return nil, mDNSMode, mdnsErr } - addr6, mdnsErr := n.ResolveUDPAddr("udp6", mdns.DefaultAddressIPv6) + addr6, mdnsErr := netTransport.ResolveUDPAddr("udp6", mdns.DefaultAddressIPv6) if mdnsErr != nil { return nil, mDNSMode, mdnsErr } @@ -78,10 +82,11 @@ func createMulticastDNS( var mdns4Err error if useV4 { var l transport.UDPConn - l, mdns4Err = n.ListenUDP("udp4", addr4) + l, mdns4Err = netTransport.ListenUDP("udp4", addr4) if mdns4Err != nil { // If ICE fails to start MulticastDNS server just warn the user and continue log.Errorf("Failed to enable mDNS over IPv4: (%s)", mdns4Err) + return nil, MulticastDNSModeDisabled, nil } pktConnV4 = ipv4.NewPacketConn(l) @@ -91,9 +96,10 @@ func createMulticastDNS( var mdns6Err error if useV6 { var l transport.UDPConn - l, mdns6Err = n.ListenUDP("udp6", addr6) + l, mdns6Err = netTransport.ListenUDP("udp6", addr6) if mdns6Err != nil { log.Errorf("Failed to enable mDNS over IPv6: (%s)", mdns6Err) + return nil, MulticastDNSModeDisabled, nil } pktConnV6 = ipv6.NewPacketConn(l) @@ -119,6 +125,7 @@ func createMulticastDNS( Interfaces: ifcs, IncludeLoopback: includeLoopback, }) + return conn, mDNSMode, err case MulticastDNSModeQueryAndGather: conn, err := mdns.Server(pktConnV4, pktConnV6, &mdns.Config{ @@ -126,6 +133,7 @@ func createMulticastDNS( IncludeLoopback: includeLoopback, LocalNames: []string{mDNSName}, }) + return conn, mDNSMode, err default: return nil, mDNSMode, nil diff --git a/net.go b/net.go index e60e646..54ec4ca 100644 --- a/net.go +++ b/net.go @@ -24,6 +24,7 @@ func isSupportedIPv6Partial(ip net.IP) bool { ip[0] == 0xfe && ip[1]&0xc0 == 0xc0 { // !(IPv6 site-local unicast) return false } + return true } @@ -33,10 +34,11 @@ func isZeros(ip net.IP) bool { return false } } + return true } -//nolint:gocognit +//nolint:gocognit,cyclop func localInterfaces( n transport.Net, interfaceFilter func(string) (keep bool), @@ -114,27 +116,35 @@ func localInterfaces( filteredIfaces = append(filteredIfaces, ifaceCopy) } } + return filteredIfaces, ipAddrs, nil } -func listenUDPInPortRange(n transport.Net, log logging.LeveledLogger, portMax, portMin int, network string, lAddr *net.UDPAddr) (transport.UDPConn, error) { +//nolint:cyclop +func listenUDPInPortRange( + netTransport transport.Net, + log logging.LeveledLogger, + portMax, portMin int, + network string, + lAddr *net.UDPAddr, +) (transport.UDPConn, error) { if (lAddr.Port != 0) || ((portMin == 0) && (portMax == 0)) { - return n.ListenUDP(network, lAddr) + return netTransport.ListenUDP(network, lAddr) } - var i, j int - i = portMin - if i == 0 { - i = 1024 // Start at 1024 which is non-privileged + + if portMin == 0 { + portMin = 1024 // Start at 1024 which is non-privileged } - j = portMax - if j == 0 { - j = 0xFFFF + + if portMax == 0 { + portMax = 0xFFFF } - if i > j { + + if portMin > portMax { return nil, ErrPort } - portStart := globalMathRandomGenerator.Intn(j-i+1) + i + portStart := globalMathRandomGenerator.Intn(portMax-portMin+1) + portMin portCurrent := portStart for { addr := &net.UDPAddr{ @@ -143,18 +153,19 @@ func listenUDPInPortRange(n transport.Net, log logging.LeveledLogger, portMax, p Port: portCurrent, } - c, e := n.ListenUDP(network, addr) + c, e := netTransport.ListenUDP(network, addr) if e == nil { return c, e //nolint:nilerr } log.Debugf("Failed to listen %s: %v", lAddr.String(), e) portCurrent++ - if portCurrent > j { - portCurrent = i + if portCurrent > portMax { + portCurrent = portMin } if portCurrent == portStart { break } } + return nil, ErrPort } diff --git a/net_test.go b/net_test.go index cd7d2fb..8b7692e 100644 --- a/net_test.go +++ b/net_test.go @@ -54,6 +54,7 @@ func problematicNetworkInterfaces(s string) (keep bool) { appleWirelessDirectLink := strings.Contains(s, "awdl") appleLowLatencyWLANInterface := strings.Contains(s, "llw") appleTunnelingInterface := strings.Contains(s, "utun") + return !defaultDockerBridgeNetwork && !customDockerBridgeNetwork && !accessPoint && @@ -68,5 +69,6 @@ func mustAddr(t *testing.T, ip net.IP) netip.Addr { if !ok { t.Fatal(ipConvertError{ip}) } + return addr } diff --git a/networktype.go b/networktype.go index 376b56c..af055c5 100644 --- a/networktype.go +++ b/networktype.go @@ -27,7 +27,7 @@ func supportedNetworkTypes() []NetworkType { } } -// NetworkType represents the type of network +// NetworkType represents the type of network. type NetworkType int const ( @@ -69,7 +69,7 @@ func (t NetworkType) IsTCP() bool { return t == NetworkTypeTCP4 || t == NetworkTypeTCP6 } -// NetworkShort returns the short network description +// NetworkShort returns the short network description. func (t NetworkType) NetworkShort() string { switch t { case NetworkTypeUDP4, NetworkTypeUDP6: @@ -81,7 +81,7 @@ func (t NetworkType) NetworkShort() string { } } -// IsReliable returns true if the network is reliable +// IsReliable returns true if the network is reliable. func (t NetworkType) IsReliable() bool { switch t { case NetworkTypeUDP4, NetworkTypeUDP6: @@ -89,6 +89,7 @@ func (t NetworkType) IsReliable() bool { case NetworkTypeTCP4, NetworkTypeTCP6: return true } + return false } @@ -100,6 +101,7 @@ func (t NetworkType) IsIPv4() bool { case NetworkTypeUDP6, NetworkTypeTCP6: return false } + return false } @@ -111,6 +113,7 @@ func (t NetworkType) IsIPv6() bool { case NetworkTypeUDP6, NetworkTypeTCP6: return true } + return false } @@ -124,12 +127,14 @@ func determineNetworkType(network string, ip netip.Addr) (NetworkType, error) { if ip.Is4() { return NetworkTypeUDP4, nil } + return NetworkTypeUDP6, nil case strings.HasPrefix(strings.ToLower(network), tcp): if ip.Is4() { return NetworkTypeTCP4, nil } + return NetworkTypeTCP6, nil } diff --git a/priority.go b/priority.go index f49df7f..f8c8740 100644 --- a/priority.go +++ b/priority.go @@ -19,6 +19,7 @@ func (p PriorityAttr) AddTo(m *stun.Message) error { v := make([]byte, prioritySize) binary.BigEndian.PutUint32(v, uint32(p)) m.Add(stun.AttrPriority, v) + return nil } @@ -32,5 +33,6 @@ func (p *PriorityAttr) GetFrom(m *stun.Message) error { return err } *p = PriorityAttr(binary.BigEndian.Uint32(v)) + return nil } diff --git a/priority_test.go b/priority_test.go index 82694b4..b6b2c5d 100644 --- a/priority_test.go +++ b/priority_test.go @@ -12,11 +12,11 @@ import ( func TestPriority_GetFrom(t *testing.T) { //nolint:dupl m := new(stun.Message) - var p PriorityAttr - if err := p.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) { + var priority PriorityAttr + if err := priority.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) { t.Error("unexpected error") } - if err := m.Build(stun.BindingRequest, &p); err != nil { + if err := m.Build(stun.BindingRequest, &priority); err != nil { t.Error(err) } m1 := new(stun.Message) @@ -27,7 +27,7 @@ func TestPriority_GetFrom(t *testing.T) { //nolint:dupl if err := p1.GetFrom(m1); err != nil { t.Error(err) } - if p1 != p { + if p1 != priority { t.Error("not equal") } t.Run("IncorrectSize", func(t *testing.T) { diff --git a/rand_test.go b/rand_test.go index ecc7053..9dd1187 100644 --- a/rand_test.go +++ b/rand_test.go @@ -23,21 +23,27 @@ func TestRandomGeneratorCollision(t *testing.T) { }, "PWD": { gen: func(t *testing.T) string { + t.Helper() + s, err := generatePwd() require.NoError(t, err) + return s }, }, "Ufrag": { gen: func(t *testing.T) string { + t.Helper() + s, err := generateUFrag() require.NoError(t, err) + return s }, }, } - const N = 100 + const num = 100 const iteration = 100 for name, testCase := range testCases { @@ -47,9 +53,9 @@ func TestRandomGeneratorCollision(t *testing.T) { var wg sync.WaitGroup var mu sync.Mutex - rands := make([]string, 0, N) + rands := make([]string, 0, num) - for i := 0; i < N; i++ { + for i := 0; i < num; i++ { wg.Add(1) go func() { r := testCase.gen(t) @@ -61,12 +67,12 @@ func TestRandomGeneratorCollision(t *testing.T) { } wg.Wait() - if len(rands) != N { + if len(rands) != num { t.Fatal("Failed to generate randoms") } - for i := 0; i < N; i++ { - for j := i + 1; j < N; j++ { + for i := 0; i < num; i++ { + for j := i + 1; j < num; j++ { if rands[i] == rands[j] { t.Fatalf("generateRandString caused collision: %s == %s", rands[i], rands[j]) } diff --git a/role.go b/role.go index e9a7bda..49f6896 100644 --- a/role.go +++ b/role.go @@ -26,6 +26,7 @@ func (r *Role) UnmarshalText(text []byte) error { default: return fmt.Errorf("%w %q", errUnknownRole, text) } + return nil } diff --git a/selection.go b/selection.go index 9aa4cad..c5fce39 100644 --- a/selection.go +++ b/selection.go @@ -44,6 +44,7 @@ func (s *controllingSelector) isNominatable(c Candidate) bool { } s.log.Errorf("Invalid candidate type: %s", c.Type()) + return false } @@ -63,6 +64,7 @@ func (s *controllingSelector) ContactCandidates() { p.nominated = true s.nominatedPair = p s.nominatePair(p) + return } s.agent.pingAllCandidates() @@ -84,6 +86,7 @@ func (s *controllingSelector) nominatePair(pair *CandidatePair) { ) if err != nil { s.log.Error(err.Error()) + return } @@ -91,30 +94,35 @@ func (s *controllingSelector) nominatePair(pair *CandidatePair) { s.agent.sendBindingRequest(msg, pair.Local, pair.Remote) } -func (s *controllingSelector) HandleBindingRequest(m *stun.Message, local, remote Candidate) { - s.agent.sendBindingSuccess(m, local, remote) +func (s *controllingSelector) HandleBindingRequest(message *stun.Message, local, remote Candidate) { //nolint:cyclop + s.agent.sendBindingSuccess(message, local, remote) - p := s.agent.findPair(local, remote) + pair := s.agent.findPair(local, remote) - if p == nil { + if pair == nil { s.agent.addPair(local, remote) + return } - if p.state == CandidatePairStateSucceeded && s.nominatedPair == nil && s.agent.getSelectedPair() == nil { + if pair.state == CandidatePairStateSucceeded && s.nominatedPair == nil && s.agent.getSelectedPair() == nil { bestPair := s.agent.getBestAvailableCandidatePair() if bestPair == nil { s.log.Tracef("No best pair available") - } else if bestPair.equal(p) && s.isNominatable(p.Local) && s.isNominatable(p.Remote) { - s.log.Tracef("The candidate (%s, %s) is the best candidate available, marking it as nominated", p.Local, p.Remote) - s.nominatedPair = p - s.nominatePair(p) + } else if bestPair.equal(pair) && s.isNominatable(pair.Local) && s.isNominatable(pair.Remote) { + s.log.Tracef( + "The candidate (%s, %s) is the best candidate available, marking it as nominated", + pair.Local, + pair.Remote, + ) + s.nominatedPair = pair + s.nominatePair(pair) } } if s.agent.userBindingRequestHandler != nil { - if shouldSwitch := s.agent.userBindingRequestHandler(m, local, remote, p); shouldSwitch { - s.agent.setSelectedPair(p) + if shouldSwitch := s.agent.userBindingRequestHandler(message, local, remote, pair); shouldSwitch { + s.agent.setSelectedPair(pair) } } } @@ -123,6 +131,7 @@ func (s *controllingSelector) HandleSuccessResponse(m *stun.Message, local, remo ok, pendingRequest, rtt := s.agent.handleInboundBindingSuccess(m.TransactionID) if !ok { s.log.Warnf("Discard message from (%s), unknown TransactionID 0x%x", remote, m.TransactionID) + return } @@ -131,26 +140,32 @@ func (s *controllingSelector) HandleSuccessResponse(m *stun.Message, local, remo // Assert that NAT is not symmetric // https://tools.ietf.org/html/rfc8445#section-7.2.5.2.1 if !addrEqual(transactionAddr, remoteAddr) { - s.log.Debugf("Discard message: transaction source and destination does not match expected(%s), actual(%s)", transactionAddr, remote) + s.log.Debugf( + "Discard message: transaction source and destination does not match expected(%s), actual(%s)", + transactionAddr, + remote, + ) + return } s.log.Tracef("Inbound STUN (SuccessResponse) from %s to %s", remote, local) - p := s.agent.findPair(local, remote) + pair := s.agent.findPair(local, remote) - if p == nil { + if pair == nil { // This shouldn't happen s.log.Error("Success response from invalid candidate pair") + return } - p.state = CandidatePairStateSucceeded - s.log.Tracef("Found valid candidate pair: %s", p) + pair.state = CandidatePairStateSucceeded + s.log.Tracef("Found valid candidate pair: %s", pair) if pendingRequest.isUseCandidate && s.agent.getSelectedPair() == nil { - s.agent.setSelectedPair(p) + s.agent.setSelectedPair(pair) } - p.UpdateRoundTripTime(rtt) + pair.UpdateRoundTripTime(rtt) } func (s *controllingSelector) PingCandidate(local, remote Candidate) { @@ -163,6 +178,7 @@ func (s *controllingSelector) PingCandidate(local, remote Candidate) { ) if err != nil { s.log.Error(err.Error()) + return } @@ -198,6 +214,7 @@ func (s *controlledSelector) PingCandidate(local, remote Candidate) { ) if err != nil { s.log.Error(err.Error()) + return } @@ -216,6 +233,7 @@ func (s *controlledSelector) HandleSuccessResponse(m *stun.Message, local, remot ok, pendingRequest, rtt := s.agent.handleInboundBindingSuccess(m.TransactionID) if !ok { s.log.Warnf("Discard message from (%s), unknown TransactionID 0x%x", remote, m.TransactionID) + return } @@ -224,52 +242,62 @@ func (s *controlledSelector) HandleSuccessResponse(m *stun.Message, local, remot // Assert that NAT is not symmetric // https://tools.ietf.org/html/rfc8445#section-7.2.5.2.1 if !addrEqual(transactionAddr, remoteAddr) { - s.log.Debugf("Discard message: transaction source and destination does not match expected(%s), actual(%s)", transactionAddr, remote) + s.log.Debugf( + "Discard message: transaction source and destination does not match expected(%s), actual(%s)", + transactionAddr, + remote, + ) + return } s.log.Tracef("Inbound STUN (SuccessResponse) from %s to %s", remote, local) - p := s.agent.findPair(local, remote) - if p == nil { + pair := s.agent.findPair(local, remote) + if pair == nil { // This shouldn't happen s.log.Error("Success response from invalid candidate pair") + return } - p.state = CandidatePairStateSucceeded - s.log.Tracef("Found valid candidate pair: %s", p) - if p.nominateOnBindingSuccess { + pair.state = CandidatePairStateSucceeded + s.log.Tracef("Found valid candidate pair: %s", pair) + if pair.nominateOnBindingSuccess { if selectedPair := s.agent.getSelectedPair(); selectedPair == nil || - (selectedPair != p && (!s.agent.needsToCheckPriorityOnNominated() || selectedPair.priority() <= p.priority())) { - s.agent.setSelectedPair(p) - } else if selectedPair != p { - s.log.Tracef("Ignore nominate new pair %s, already nominated pair %s", p, selectedPair) + (selectedPair != pair && + (!s.agent.needsToCheckPriorityOnNominated() || selectedPair.priority() <= pair.priority())) { + s.agent.setSelectedPair(pair) + } else if selectedPair != pair { + s.log.Tracef("Ignore nominate new pair %s, already nominated pair %s", pair, selectedPair) } } - p.UpdateRoundTripTime(rtt) + pair.UpdateRoundTripTime(rtt) } -func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote Candidate) { - p := s.agent.findPair(local, remote) - if p == nil { - p = s.agent.addPair(local, remote) +func (s *controlledSelector) HandleBindingRequest(message *stun.Message, local, remote Candidate) { //nolint:cyclop + pair := s.agent.findPair(local, remote) + if pair == nil { + pair = s.agent.addPair(local, remote) } - if m.Contains(stun.AttrUseCandidate) { + if message.Contains(stun.AttrUseCandidate) { //nolint:nestif // https://tools.ietf.org/html/rfc8445#section-7.3.1.5 - if p.state == CandidatePairStateSucceeded { + if pair.state == CandidatePairStateSucceeded { // If the state of this pair is Succeeded, it means that the check // previously sent by this pair produced a successful response and // generated a valid pair (Section 7.2.5.3.2). The agent sets the // nominated flag value of the valid pair to true. selectedPair := s.agent.getSelectedPair() - if selectedPair == nil || (selectedPair != p && (!s.agent.needsToCheckPriorityOnNominated() || selectedPair.priority() <= p.priority())) { - s.agent.setSelectedPair(p) - } else if selectedPair != p { - s.log.Tracef("Ignore nominate new pair %s, already nominated pair %s", p, selectedPair) + if selectedPair == nil || + (selectedPair != pair && + (!s.agent.needsToCheckPriorityOnNominated() || + selectedPair.priority() <= pair.priority())) { + s.agent.setSelectedPair(pair) + } else if selectedPair != pair { + s.log.Tracef("Ignore nominate new pair %s, already nominated pair %s", pair, selectedPair) } } else { // If the received Binding request triggered a new check to be @@ -280,16 +308,16 @@ func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote // MUST remove the candidate pair from the valid list, set the // candidate pair state to Failed, and set the checklist state to // Failed. - p.nominateOnBindingSuccess = true + pair.nominateOnBindingSuccess = true } } - s.agent.sendBindingSuccess(m, local, remote) + s.agent.sendBindingSuccess(message, local, remote) s.PingCandidate(local, remote) if s.agent.userBindingRequestHandler != nil { - if shouldSwitch := s.agent.userBindingRequestHandler(m, local, remote, p); shouldSwitch { - s.agent.setSelectedPair(p) + if shouldSwitch := s.agent.userBindingRequestHandler(message, local, remote, pair); shouldSwitch { + s.agent.setSelectedPair(pair) } } } @@ -298,7 +326,7 @@ type liteSelector struct { pairCandidateSelector } -// A lite selector should not contact candidates +// A lite selector should not contact candidates. func (s *liteSelector) ContactCandidates() { if _, ok := s.pairCandidateSelector.(*controllingSelector); ok { //nolint:godox diff --git a/selection_test.go b/selection_test.go index 1c9a56a..7f4e5f3 100644 --- a/selection_test.go +++ b/selection_test.go @@ -23,6 +23,8 @@ import ( ) func sendUntilDone(t *testing.T, writingConn, readingConn net.Conn, maxAttempts int) bool { + t.Helper() + testMessage := []byte("Hello World") testBuffer := make([]byte, len(testMessage)) @@ -73,6 +75,7 @@ func TestBindingRequestHandler(t *testing.T) { CheckInterval: &oneHour, BindingRequestHandler: func(_ *stun.Message, _, _ Candidate, _ *CandidatePair) bool { controlledLoggingFired.Store(true) + return false }, }) @@ -87,6 +90,7 @@ func TestBindingRequestHandler(t *testing.T) { BindingRequestHandler: func(_ *stun.Message, _, _ Candidate, _ *CandidatePair) bool { // Don't switch candidate pair until we are ready val, ok := switchToNewCandidatePair.Load().(bool) + return ok && val }, }) diff --git a/stats.go b/stats.go index 9b83bea..8a08f67 100644 --- a/stats.go +++ b/stats.go @@ -7,7 +7,7 @@ import ( "time" ) -// CandidatePairStats contains ICE candidate pair statistics +// CandidatePairStats contains ICE candidate pair statistics. type CandidatePairStats struct { // Timestamp is the timestamp associated with this object. Timestamp time.Time diff --git a/tcp_mux.go b/tcp_mux.go index ef6f038..829eac1 100644 --- a/tcp_mux.go +++ b/tcp_mux.go @@ -79,20 +79,20 @@ func NewTCPMuxDefault(params TCPMuxParams) *TCPMuxDefault { params.AliveDurationForConnFromStun = 30 * time.Second } - m := &TCPMuxDefault{ + mux := &TCPMuxDefault{ params: ¶ms, connsIPv4: map[string]map[ipAddr]*tcpPacketConn{}, connsIPv6: map[string]map[ipAddr]*tcpPacketConn{}, } - m.wg.Add(1) + mux.wg.Add(1) go func() { - defer m.wg.Done() - m.start() + defer mux.wg.Done() + mux.start() }() - return m + return mux } func (m *TCPMuxDefault) start() { @@ -101,6 +101,7 @@ func (m *TCPMuxDefault) start() { conn, err := m.params.Listener.Accept() if err != nil { m.params.Logger.Infof("Error accepting connection: %s", err) + return } @@ -130,6 +131,7 @@ func (m *TCPMuxDefault) GetConnByUfrag(ufrag string, isIPv6 bool, local net.IP) if conn, ok := m.getConn(ufrag, isIPv6, local); ok { conn.ClearAliveTimer() + return conn, nil } @@ -191,12 +193,17 @@ func (m *TCPMuxDefault) closeAndLogError(closer io.Closer) { } } -func (m *TCPMuxDefault) handleConn(conn net.Conn) { +func (m *TCPMuxDefault) handleConn(conn net.Conn) { //nolint:cyclop buf := make([]byte, 512) if m.params.FirstStunBindTimeout > 0 { if err := conn.SetReadDeadline(time.Now().Add(m.params.FirstStunBindTimeout)); err != nil { - m.params.Logger.Warnf("Failed to set read deadline for first STUN message: %s to %s , err: %s", conn.RemoteAddr(), conn.LocalAddr(), err) + m.params.Logger.Warnf( + "Failed to set read deadline for first STUN message: %s to %s , err: %s", + conn.RemoteAddr(), + conn.LocalAddr(), + err, + ) } } n, err := readStreamingPacket(conn, buf) @@ -207,6 +214,7 @@ func (m *TCPMuxDefault) handleConn(conn net.Conn) { m.params.Logger.Warnf("Error reading first packet from %s: %s", conn.RemoteAddr(), err) } m.closeAndLogError(conn) + return } if err = conn.SetReadDeadline(time.Time{}); err != nil { @@ -223,12 +231,14 @@ func (m *TCPMuxDefault) handleConn(conn net.Conn) { if err = msg.Decode(); err != nil { m.closeAndLogError(conn) m.params.Logger.Warnf("Failed to handle decode ICE from %s to %s: %v", conn.RemoteAddr(), conn.LocalAddr(), err) + return } if m == nil || msg.Type.Method != stun.MethodBinding { // Not a STUN m.closeAndLogError(conn) m.params.Logger.Warnf("Not a STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr()) + return } @@ -239,7 +249,12 @@ func (m *TCPMuxDefault) handleConn(conn net.Conn) { attr, err := msg.Get(stun.AttrUsername) if err != nil { m.closeAndLogError(conn) - m.params.Logger.Warnf("No Username attribute in STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr()) + m.params.Logger.Warnf( + "No Username attribute in STUN message from %s to %s", + conn.RemoteAddr(), + conn.LocalAddr(), + ) + return } @@ -249,7 +264,12 @@ func (m *TCPMuxDefault) handleConn(conn net.Conn) { host, _, err := net.SplitHostPort(conn.RemoteAddr().String()) if err != nil { m.closeAndLogError(conn) - m.params.Logger.Warnf("Failed to get host in STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr()) + m.params.Logger.Warnf( + "Failed to get host in STUN message from %s to %s", + conn.RemoteAddr(), + conn.LocalAddr(), + ) + return } @@ -258,7 +278,12 @@ func (m *TCPMuxDefault) handleConn(conn net.Conn) { localAddr, ok := conn.LocalAddr().(*net.TCPAddr) if !ok { m.closeAndLogError(conn) - m.params.Logger.Warnf("Failed to get local tcp address in STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr()) + m.params.Logger.Warnf( + "Failed to get local tcp address in STUN message from %s to %s", + conn.RemoteAddr(), + conn.LocalAddr(), + ) + return } m.mu.Lock() @@ -269,7 +294,12 @@ func (m *TCPMuxDefault) handleConn(conn net.Conn) { if err != nil { m.mu.Unlock() m.closeAndLogError(conn) - m.params.Logger.Warnf("Failed to create packetConn for STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr()) + m.params.Logger.Warnf( + "Failed to create packetConn for STUN message from %s to %s", + conn.RemoteAddr(), + conn.LocalAddr(), + ) + return } } @@ -277,7 +307,13 @@ func (m *TCPMuxDefault) handleConn(conn net.Conn) { if err := packetConn.AddConn(conn, buf); err != nil { m.closeAndLogError(conn) - m.params.Logger.Warnf("Error adding conn to tcpPacketConn from %s to %s: %s", conn.RemoteAddr(), conn.LocalAddr(), err) + m.params.Logger.Warnf( + "Error adding conn to tcpPacketConn from %s to %s: %s", + conn.RemoteAddr(), + conn.LocalAddr(), + err, + ) + return } } @@ -428,7 +464,7 @@ func readStreamingPacket(conn net.Conn, buf []byte) (int, error) { func writeStreamingPacket(conn net.Conn, buf []byte) (int, error) { bufCopy := make([]byte, streamingPacketHeaderLen+len(buf)) - binary.BigEndian.PutUint16(bufCopy, uint16(len(buf))) + binary.BigEndian.PutUint16(bufCopy, uint16(len(buf))) //nolint:gosec // G115 copy(bufCopy[2:], buf) n, err := conn.Write(bufCopy) diff --git a/tcp_mux_multi.go b/tcp_mux_multi.go index 71fc570..225cefe 100644 --- a/tcp_mux_multi.go +++ b/tcp_mux_multi.go @@ -40,6 +40,7 @@ func (m *MultiTCPMuxDefault) GetConnByUfrag(ufrag string, isIPv6 bool, local net if len(m.muxes) == 0 { return nil, errNoTCPMuxAvailable } + return m.muxes[0].GetConnByUfrag(ufrag, isIPv6, local) } @@ -51,7 +52,7 @@ func (m *MultiTCPMuxDefault) RemoveConnByUfrag(ufrag string) { } } -// GetAllConns returns a PacketConn for each underlying TCPMux +// GetAllConns returns a PacketConn for each underlying TCPMux. func (m *MultiTCPMuxDefault) GetAllConns(ufrag string, isIPv6 bool, local net.IP) ([]net.PacketConn, error) { if len(m.muxes) == 0 { // Make sure that we either return at least one connection or an error. @@ -68,10 +69,11 @@ func (m *MultiTCPMuxDefault) GetAllConns(ufrag string, isIPv6 bool, local net.IP conns = append(conns, conn) } } + return conns, nil } -// Close the multi mux, no further connections could be created +// Close the multi mux, no further connections could be created. func (m *MultiTCPMuxDefault) Close() error { var err error for _, mux := range m.muxes { @@ -79,5 +81,6 @@ func (m *MultiTCPMuxDefault) Close() error { err = e } } + return err } diff --git a/tcp_packet_conn.go b/tcp_packet_conn.go index 283f1da..1d1dffc 100644 --- a/tcp_packet_conn.go +++ b/tcp_packet_conn.go @@ -36,6 +36,7 @@ func newBufferedConn(conn net.Conn, bufSize int, logger logging.LeveledLogger) n } go bc.writeProcess() + return bc } @@ -44,6 +45,7 @@ func (bc *bufferedConn) Write(b []byte) (int, error) { if err != nil { return n, err } + return n, nil } @@ -57,11 +59,13 @@ func (bc *bufferedConn) writeProcess() { if err != nil { bc.logger.Warnf("Failed to read from buffer: %s", err) + continue } if _, err := bc.Conn.Write(pktBuf[:n]); err != nil { bc.logger.Warnf("Failed to write: %s", err) + continue } } @@ -70,6 +74,7 @@ func (bc *bufferedConn) writeProcess() { func (bc *bufferedConn) Close() error { atomic.StoreInt32(&bc.closed, 1) _ = bc.buf.Close() + return bc.Conn.Close() } @@ -103,7 +108,7 @@ type tcpPacketParams struct { } func newTCPPacketConn(params tcpPacketParams) *tcpPacketConn { - p := &tcpPacketConn{ + packet := &tcpPacketConn{ params: ¶ms, conns: map[string]net.Conn{}, @@ -113,13 +118,13 @@ func newTCPPacketConn(params tcpPacketParams) *tcpPacketConn { } if params.AliveDuration > 0 { - p.aliveTimer = time.AfterFunc(params.AliveDuration, func() { - p.params.Logger.Warn("close tcp packet conn by alive timeout") - _ = p.Close() + packet.aliveTimer = time.AfterFunc(params.AliveDuration, func() { + packet.params.Logger.Warn("close tcp packet conn by alive timeout") + _ = packet.Close() }) } - return p + return packet } func (t *tcpPacketConn) ClearAliveTimer() { @@ -131,7 +136,12 @@ func (t *tcpPacketConn) ClearAliveTimer() { } func (t *tcpPacketConn) AddConn(conn net.Conn, firstPacketData []byte) error { - t.params.Logger.Infof("Added connection: %s remote %s to local %s", conn.RemoteAddr().Network(), conn.RemoteAddr(), conn.LocalAddr()) + t.params.Logger.Infof( + "Added connection: %s remote %s to local %s", + conn.RemoteAddr().Network(), + conn.RemoteAddr(), + conn.LocalAddr(), + ) t.mu.Lock() defer t.mu.Unlock() @@ -183,6 +193,7 @@ func (t *tcpPacketConn) startReading(conn net.Conn) { if last || !(errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed)) { t.handleRecv(streamingPacket{nil, conn.RemoteAddr(), err}) } + return } @@ -236,6 +247,7 @@ func (t *tcpPacketConn) ReadFrom(b []byte) (n int, rAddr net.Addr, err error) { n = len(pkt.Data) copy(b, pkt.Data[:n]) + return n, pkt.RAddr, err } @@ -252,6 +264,7 @@ func (t *tcpPacketConn) WriteTo(buf []byte, rAddr net.Addr) (n int, err error) { n, err = writeStreamingPacket(conn, buf) if err != nil { t.params.Logger.Tracef("%w %s", errWrite, rAddr) + return n, err } @@ -272,6 +285,7 @@ func (t *tcpPacketConn) removeConn(conn net.Conn) bool { t.closeAndLogError(conn) delete(t.conns, conn.RemoteAddr().String()) + return len(t.conns) == 0 } diff --git a/transport.go b/transport.go index cf9e3f9..a28605d 100644 --- a/transport.go +++ b/transport.go @@ -32,12 +32,12 @@ type Conn struct { agent *Agent } -// BytesSent returns the number of bytes sent +// BytesSent returns the number of bytes sent. func (c *Conn) BytesSent() uint64 { return atomic.LoadUint64(&c.bytesSent) } -// BytesReceived returns the number of bytes received +// BytesReceived returns the number of bytes received. func (c *Conn) BytesReceived() uint64 { return atomic.LoadUint64(&c.bytesReceived) } @@ -74,18 +74,19 @@ func (c *Conn) Read(p []byte) (int, error) { } n, err := c.agent.buf.Read(p) - atomic.AddUint64(&c.bytesReceived, uint64(n)) + atomic.AddUint64(&c.bytesReceived, uint64(n)) //nolint:gosec // G115 + return n, err } // Write implements the Conn Write method. -func (c *Conn) Write(p []byte) (int, error) { +func (c *Conn) Write(packet []byte) (int, error) { err := c.agent.loop.Err() if err != nil { return 0, err } - if stun.IsMessage(p) { + if stun.IsMessage(packet) { return 0, errWriteSTUNMessageToIceConn } @@ -102,8 +103,9 @@ func (c *Conn) Write(p []byte) (int, error) { } } - atomic.AddUint64(&c.bytesSent, uint64(len(p))) - return pair.Write(p) + atomic.AddUint64(&c.bytesSent, uint64(len(packet))) + + return pair.Write(packet) } // Close implements the Conn Close method. It is used to close @@ -132,17 +134,17 @@ func (c *Conn) RemoteAddr() net.Addr { return pair.Remote.addr() } -// SetDeadline is a stub +// SetDeadline is a stub. func (c *Conn) SetDeadline(time.Time) error { return nil } -// SetReadDeadline is a stub +// SetReadDeadline is a stub. func (c *Conn) SetReadDeadline(time.Time) error { return nil } -// SetWriteDeadline is a stub +// SetWriteDeadline is a stub. func (c *Conn) SetWriteDeadline(time.Time) error { return nil } diff --git a/transport_test.go b/transport_test.go index 4563e57..0581c93 100644 --- a/transport_test.go +++ b/transport_test.go @@ -30,13 +30,15 @@ func TestStressDuplex(t *testing.T) { stressDuplex(t) } -func testTimeout(t *testing.T, c *Conn, timeout time.Duration) { +func testTimeout(t *testing.T, conn *Conn, timeout time.Duration) { + t.Helper() + const pollRate = 100 * time.Millisecond const margin = 20 * time.Millisecond // Allow 20msec error in time ticker := time.NewTicker(pollRate) defer func() { ticker.Stop() - err := c.Close() + err := conn.Close() if err != nil { t.Error(err) } @@ -49,8 +51,8 @@ func testTimeout(t *testing.T, c *Conn, timeout time.Duration) { var cs ConnectionState - err := c.agent.loop.Run(context.Background(), func(_ context.Context) { - cs = c.agent.connectionState + err := conn.agent.loop.Run(context.Background(), func(_ context.Context) { + cs = conn.agent.connectionState }) if err != nil { // We should never get here. @@ -63,6 +65,7 @@ func testTimeout(t *testing.T, c *Conn, timeout time.Duration) { t.Fatalf("Connection timed out %f msec early", elapsed.Seconds()*1000) } else { t.Logf("Connection timed out in %f msec", elapsed.Seconds()*1000) + return } } @@ -133,6 +136,8 @@ func TestReadClosed(t *testing.T) { } func stressDuplex(t *testing.T) { + t.Helper() + ca, cb := pipe(nil) defer func() { @@ -219,6 +224,7 @@ func connect(aAgent, bAgent *Agent) (*Conn, *Conn) { // Ensure accepted <-accepted + return aConn, bConn } @@ -288,6 +294,7 @@ func pipeWithTimeout(disconnectTimeout time.Duration, iceKeepalive time.Duration func onConnected() (func(ConnectionState), chan struct{}) { done := make(chan struct{}) + return func(state ConnectionState) { if state == ConnectionStateConnected { close(done) @@ -295,11 +302,11 @@ func onConnected() (func(ConnectionState), chan struct{}) { }, done } -func randomPort(t testing.TB) int { - t.Helper() +func randomPort(tb testing.TB) int { + tb.Helper() conn, err := net.ListenPacket("udp4", "127.0.0.1:0") if err != nil { - t.Fatalf("failed to pickPort: %v", err) + tb.Fatalf("failed to pickPort: %v", err) } defer func() { _ = conn.Close() @@ -308,7 +315,8 @@ func randomPort(t testing.TB) int { case *net.UDPAddr: return addr.Port default: - t.Fatalf("unknown addr type %T", addr) + tb.Fatalf("unknown addr type %T", addr) + return 0 } } diff --git a/transport_vnet_test.go b/transport_vnet_test.go index bc8a4f6..8eb2b6d 100644 --- a/transport_vnet_test.go +++ b/transport_vnet_test.go @@ -30,9 +30,9 @@ func TestRemoteLocalAddr(t *testing.T) { // Agent1 is behind 1:1 NAT natType1 := &vnet.NATType{Mode: vnet.NATModeNAT1To1} - v, errVnet := buildVNet(natType0, natType1) + builtVnet, errVnet := buildVNet(natType0, natType1) require.NoError(t, errVnet, "should succeed") - defer v.close() + defer builtVnet.close() stunServerURL := &stun.URI{ Scheme: stun.SchemeTypeSTUN, @@ -53,7 +53,7 @@ func TestRemoteLocalAddr(t *testing.T) { }) t.Run("Remote/Local Pair Match between Agents", func(t *testing.T) { - ca, cb := pipeWithVNet(v, + ca, cb := pipeWithVNet(builtVnet, &agentTestConfig{ urls: []*stun.URI{stunServerURL}, }, diff --git a/udp_mux.go b/udp_mux.go index 96197b3..3732b26 100644 --- a/udp_mux.go +++ b/udp_mux.go @@ -18,7 +18,7 @@ import ( "github.com/pion/transport/v3/stdnet" ) -// UDPMux allows multiple connections to go over a single UDP port +// UDPMux allows multiple connections to go over a single UDP port. type UDPMux interface { io.Closer GetConn(ufrag string, addr net.Addr) (net.PacketConn, error) @@ -26,7 +26,7 @@ type UDPMux interface { GetListenAddresses() []net.Addr } -// UDPMuxDefault is an implementation of the interface +// UDPMuxDefault is an implementation of the interface. type UDPMuxDefault struct { params UDPMuxParams @@ -60,14 +60,14 @@ type UDPMuxParams struct { Net transport.Net } -// NewUDPMuxDefault creates an implementation of UDPMux -func NewUDPMuxDefault(params UDPMuxParams) *UDPMuxDefault { +// NewUDPMuxDefault creates an implementation of UDPMux. +func NewUDPMuxDefault(params UDPMuxParams) *UDPMuxDefault { //nolint:cyclop if params.Logger == nil { params.Logger = logging.NewDefaultLoggerFactory().NewLogger("ice") } var localAddrsForUnspecified []net.Addr - if udpAddr, ok := params.UDPConn.LocalAddr().(*net.UDPAddr); !ok { + if udpAddr, ok := params.UDPConn.LocalAddr().(*net.UDPAddr); !ok { //nolint:nestif params.Logger.Errorf("LocalAddr is not a net.UDPAddr, got %T", params.UDPConn.LocalAddr()) } else if ok && udpAddr.IP.IsUnspecified() { // For unspecified addresses, the correct behavior is to return errListenUnspecified, but @@ -109,7 +109,7 @@ func NewUDPMuxDefault(params UDPMuxParams) *UDPMuxDefault { } params.UDPConnString = params.UDPConn.LocalAddr().String() - m := &UDPMuxDefault{ + mux := &UDPMuxDefault{ addressMap: map[ipPort]*udpMuxedConn{}, params: params, connsIPv4: make(map[string]*udpMuxedConn), @@ -124,17 +124,17 @@ func NewUDPMuxDefault(params UDPMuxParams) *UDPMuxDefault { localAddrsForUnspecified: localAddrsForUnspecified, } - go m.connWorker() + go mux.connWorker() - return m + return mux } -// LocalAddr returns the listening address of this UDPMuxDefault +// LocalAddr returns the listening address of this UDPMuxDefault. func (m *UDPMuxDefault) LocalAddr() net.Addr { return m.params.UDPConn.LocalAddr() } -// GetListenAddresses returns the list of addresses that this mux is listening on +// GetListenAddresses returns the list of addresses that this mux is listening on. func (m *UDPMuxDefault) GetListenAddresses() []net.Addr { if len(m.localAddrsForUnspecified) > 0 { return m.localAddrsForUnspecified @@ -143,8 +143,8 @@ func (m *UDPMuxDefault) GetListenAddresses() []net.Addr { return []net.Addr{m.LocalAddr()} } -// GetConn returns a PacketConn given the connection's ufrag and network address -// creates the connection if an existing one can't be found +// GetConn returns a PacketConn given the connection's ufrag and network address. +// creates the connection if an existing one can't be found. func (m *UDPMuxDefault) GetConn(ufrag string, addr net.Addr) (net.PacketConn, error) { // don't check addr for mux using unspecified address if len(m.localAddrsForUnspecified) == 0 && m.params.UDPConnString != addr.String() { @@ -181,11 +181,11 @@ func (m *UDPMuxDefault) GetConn(ufrag string, addr net.Addr) (net.PacketConn, er return c, nil } -// RemoveConnByUfrag stops and removes the muxed packet connection +// RemoveConnByUfrag stops and removes the muxed packet connection. func (m *UDPMuxDefault) RemoveConnByUfrag(ufrag string) { removedConns := make([]*udpMuxedConn, 0, 2) - // Keep lock section small to avoid deadlock with conn lock + // Keep lock section small to avoid deadlock with conn lock. m.mu.Lock() if c, ok := m.connsIPv4[ufrag]; ok { delete(m.connsIPv4, ufrag) @@ -198,7 +198,7 @@ func (m *UDPMuxDefault) RemoveConnByUfrag(ufrag string) { m.mu.Unlock() if len(removedConns) == 0 { - // No need to lock if no connection was found + // No need to lock if no connection was found. return } @@ -213,7 +213,7 @@ func (m *UDPMuxDefault) RemoveConnByUfrag(ufrag string) { } } -// IsClosed returns true if the mux had been closed +// IsClosed returns true if the mux had been closed. func (m *UDPMuxDefault) IsClosed() bool { select { case <-m.closedChan: @@ -223,7 +223,7 @@ func (m *UDPMuxDefault) IsClosed() bool { } } -// Close the mux, no further connections could be created +// Close the mux, no further connections could be created. func (m *UDPMuxDefault) Close() error { var err error m.closeOnce.Do(func() { @@ -244,6 +244,7 @@ func (m *UDPMuxDefault) Close() error { _ = m.params.UDPConn.Close() }) + return err } @@ -276,10 +277,11 @@ func (m *UDPMuxDefault) createMuxedConn(key string) *udpMuxedConn { LocalAddr: m.LocalAddr(), Logger: m.params.Logger, }) + return c } -func (m *UDPMuxDefault) connWorker() { +func (m *UDPMuxDefault) connWorker() { //nolint:cyclop logger := m.params.Logger defer func() { @@ -304,11 +306,13 @@ func (m *UDPMuxDefault) connWorker() { netUDPAddr, ok := addr.(*net.UDPAddr) if !ok { logger.Errorf("Underlying PacketConn did not return a UDPAddr") + return } - udpAddr, err := newIPPort(netUDPAddr.IP, netUDPAddr.Zone, uint16(netUDPAddr.Port)) + udpAddr, err := newIPPort(netUDPAddr.IP, netUDPAddr.Zone, uint16(netUDPAddr.Port)) //nolint:gosec if err != nil { logger.Errorf("Failed to create a new IP/Port host pair") + return } @@ -325,12 +329,14 @@ func (m *UDPMuxDefault) connWorker() { if err = msg.Decode(); err != nil { m.params.Logger.Warnf("Failed to handle decode ICE from %s: %v", addr.String(), err) + continue } attr, stunAttrErr := msg.Get(stun.AttrUsername) if stunAttrErr != nil { m.params.Logger.Warnf("No Username attribute in STUN message from %s", addr.String()) + continue } @@ -344,6 +350,7 @@ func (m *UDPMuxDefault) connWorker() { if destinationConn == nil { m.params.Logger.Tracef("Dropping packet from %s, addr: %s", udpAddr.addr, addr) + continue } @@ -359,6 +366,7 @@ func (m *UDPMuxDefault) getConn(ufrag string, isIPv6 bool) (val *udpMuxedConn, o } else { val, ok = m.connsIPv4[ufrag] } + return } @@ -386,7 +394,7 @@ type ipPort struct { // newIPPort create a custom type of address based on netip.Addr and // port. The underlying ip address passed is converted to IPv6 format -// to simplify ip address handling +// to simplify ip address handling. func newIPPort(ip net.IP, zone string, port uint16) (ipPort, error) { n, ok := netip.AddrFromSlice(ip.To16()) if !ok { diff --git a/udp_mux_multi.go b/udp_mux_multi.go index f8b4285..46c88bb 100644 --- a/udp_mux_multi.go +++ b/udp_mux_multi.go @@ -29,6 +29,7 @@ func NewMultiUDPMuxDefault(muxes ...UDPMux) *MultiUDPMuxDefault { addrToMux[addr.String()] = mux } } + return &MultiUDPMuxDefault{ muxes: muxes, localAddrToMux: addrToMux, @@ -42,6 +43,7 @@ func (m *MultiUDPMuxDefault) GetConn(ufrag string, addr net.Addr) (net.PacketCon if !ok { return nil, errNoUDPMuxAvailable } + return mux.GetConn(ufrag, addr) } @@ -53,7 +55,7 @@ func (m *MultiUDPMuxDefault) RemoveConnByUfrag(ufrag string) { } } -// Close the multi mux, no further connections could be created +// Close the multi mux, no further connections could be created. func (m *MultiUDPMuxDefault) Close() error { var err error for _, mux := range m.muxes { @@ -61,21 +63,23 @@ func (m *MultiUDPMuxDefault) Close() error { err = e } } + return err } -// GetListenAddresses returns the list of addresses that this mux is listening on +// GetListenAddresses returns the list of addresses that this mux is listening on. func (m *MultiUDPMuxDefault) GetListenAddresses() []net.Addr { addrs := make([]net.Addr, 0, len(m.localAddrToMux)) for _, mux := range m.muxes { addrs = append(addrs, mux.GetListenAddresses()...) } + return addrs } // NewMultiUDPMuxFromPort creates an instance of MultiUDPMuxDefault that // listen all interfaces on the provided port. -func NewMultiUDPMuxFromPort(port int, opts ...UDPMuxFromPortOption) (*MultiUDPMuxDefault, error) { +func NewMultiUDPMuxFromPort(port int, opts ...UDPMuxFromPortOption) (*MultiUDPMuxDefault, error) { //nolint:cyclop params := multiUDPMuxFromPortParam{ networks: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}, } @@ -104,6 +108,7 @@ func NewMultiUDPMuxFromPort(port int, opts ...UDPMuxFromPortOption) (*MultiUDPMu }) if listenErr != nil { err = listenErr + break } if params.readBufferSize > 0 { @@ -119,6 +124,7 @@ func NewMultiUDPMuxFromPort(port int, opts ...UDPMuxFromPortOption) (*MultiUDPMu for _, conn := range conns { _ = conn.Close() } + return nil, err } @@ -135,7 +141,7 @@ func NewMultiUDPMuxFromPort(port int, opts ...UDPMuxFromPortOption) (*MultiUDPMu return NewMultiUDPMuxDefault(muxes...), nil } -// UDPMuxFromPortOption provide options for NewMultiUDPMuxFromPort +// UDPMuxFromPortOption provide options for NewMultiUDPMuxFromPort. type UDPMuxFromPortOption interface { apply(*multiUDPMuxFromPortParam) } @@ -159,7 +165,7 @@ func (o *udpMuxFromPortOption) apply(p *multiUDPMuxFromPortParam) { o.f(p) } -// UDPMuxFromPortWithInterfaceFilter set the filter to filter out interfaces that should not be used +// UDPMuxFromPortWithInterfaceFilter set the filter to filter out interfaces that should not be used. func UDPMuxFromPortWithInterfaceFilter(f func(string) (keep bool)) UDPMuxFromPortOption { return &udpMuxFromPortOption{ f: func(p *multiUDPMuxFromPortParam) { @@ -168,7 +174,7 @@ func UDPMuxFromPortWithInterfaceFilter(f func(string) (keep bool)) UDPMuxFromPor } } -// UDPMuxFromPortWithIPFilter set the filter to filter out IP addresses that should not be used +// UDPMuxFromPortWithIPFilter set the filter to filter out IP addresses that should not be used. func UDPMuxFromPortWithIPFilter(f func(ip net.IP) (keep bool)) UDPMuxFromPortOption { return &udpMuxFromPortOption{ f: func(p *multiUDPMuxFromPortParam) { @@ -177,7 +183,7 @@ func UDPMuxFromPortWithIPFilter(f func(ip net.IP) (keep bool)) UDPMuxFromPortOpt } } -// UDPMuxFromPortWithNetworks set the networks that should be used. default is both IPv4 and IPv6 +// UDPMuxFromPortWithNetworks set the networks that should be used. default is both IPv4 and IPv6. func UDPMuxFromPortWithNetworks(networks ...NetworkType) UDPMuxFromPortOption { return &udpMuxFromPortOption{ f: func(p *multiUDPMuxFromPortParam) { @@ -186,7 +192,7 @@ func UDPMuxFromPortWithNetworks(networks ...NetworkType) UDPMuxFromPortOption { } } -// UDPMuxFromPortWithReadBufferSize set the UDP connection read buffer size +// UDPMuxFromPortWithReadBufferSize set the UDP connection read buffer size. func UDPMuxFromPortWithReadBufferSize(size int) UDPMuxFromPortOption { return &udpMuxFromPortOption{ f: func(p *multiUDPMuxFromPortParam) { @@ -195,7 +201,7 @@ func UDPMuxFromPortWithReadBufferSize(size int) UDPMuxFromPortOption { } } -// UDPMuxFromPortWithWriteBufferSize set the UDP connection write buffer size +// UDPMuxFromPortWithWriteBufferSize set the UDP connection write buffer size. func UDPMuxFromPortWithWriteBufferSize(size int) UDPMuxFromPortOption { return &udpMuxFromPortOption{ f: func(p *multiUDPMuxFromPortParam) { @@ -204,7 +210,7 @@ func UDPMuxFromPortWithWriteBufferSize(size int) UDPMuxFromPortOption { } } -// UDPMuxFromPortWithLogger set the logger for the created UDPMux +// UDPMuxFromPortWithLogger set the logger for the created UDPMux. func UDPMuxFromPortWithLogger(logger logging.LeveledLogger) UDPMuxFromPortOption { return &udpMuxFromPortOption{ f: func(p *multiUDPMuxFromPortParam) { @@ -213,7 +219,7 @@ func UDPMuxFromPortWithLogger(logger logging.LeveledLogger) UDPMuxFromPortOption } } -// UDPMuxFromPortWithLoopback set loopback interface should be included +// UDPMuxFromPortWithLoopback set loopback interface should be included. func UDPMuxFromPortWithLoopback() UDPMuxFromPortOption { return &udpMuxFromPortOption{ f: func(p *multiUDPMuxFromPortParam) { diff --git a/udp_mux_multi_test.go b/udp_mux_multi_test.go index f5611be..0986fc1 100644 --- a/udp_mux_multi_test.go +++ b/udp_mux_multi_test.go @@ -79,6 +79,8 @@ func TestMultiUDPMux(t *testing.T) { } func testMultiUDPMuxConnections(t *testing.T, udpMuxMulti *MultiUDPMuxDefault, ufrag string, network string) { + t.Helper() + addrs := udpMuxMulti.GetListenAddresses() pktConns := make([]net.PacketConn, 0, len(addrs)) for _, addr := range addrs { diff --git a/udp_mux_test.go b/udp_mux_test.go index fa24398..3a293ff 100644 --- a/udp_mux_test.go +++ b/udp_mux_test.go @@ -20,7 +20,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestUDPMux(t *testing.T) { +func TestUDPMux(t *testing.T) { //nolint:cyclop defer test.CheckRoutines(t)() defer test.TimeOut(time.Second * 30).Stop() @@ -127,6 +127,8 @@ func TestUDPMux(t *testing.T) { } func testMuxConnection(t *testing.T, udpMux *UDPMuxDefault, ufrag string, network string) { + t.Helper() + pktConn, err := udpMux.GetConn(ufrag, udpMux.LocalAddr()) require.NoError(t, err, "error retrieving muxed connection for ufrag") defer func() { @@ -145,6 +147,8 @@ func testMuxConnection(t *testing.T, udpMux *UDPMuxDefault, ufrag string, networ } func testMuxConnectionPair(t *testing.T, pktConn net.PacketConn, remoteConn *net.UDPConn, ufrag string) { + t.Helper() + // Initial messages are dropped _, err := remoteConn.Write([]byte("dropped bytes")) require.NoError(t, err) @@ -222,7 +226,7 @@ func testMuxConnectionPair(t *testing.T, pktConn net.PacketConn, remoteConn *net require.NoError(t, err) h := sha256.Sum256(buf[36:]) copy(buf[4:36], h[:]) - binary.LittleEndian.PutUint32(buf[0:4], uint32(sequence)) + binary.LittleEndian.PutUint32(buf[0:4], uint32(sequence)) //nolint:gosec // G115 _, err = remoteConn.Write(buf) require.NoError(t, err) @@ -238,6 +242,8 @@ func testMuxConnectionPair(t *testing.T, pktConn net.PacketConn, remoteConn *net } func verifyPacket(t *testing.T, b []byte, nextSeq uint32) { + t.Helper() + readSeq := binary.LittleEndian.Uint32(b[0:4]) require.Equal(t, nextSeq, readSeq) h := sha256.Sum256(b[36:]) diff --git a/udp_mux_universal.go b/udp_mux_universal.go index 79c12fb..50c80af 100644 --- a/udp_mux_universal.go +++ b/udp_mux_universal.go @@ -29,7 +29,8 @@ type UniversalUDPMuxDefault struct { *UDPMuxDefault params UniversalUDPMuxParams - // Since we have a shared socket, for srflx candidates it makes sense to have a shared mapped address across all the agents + // Since we have a shared socket, for srflx candidates it makes sense + // to have a shared mapped address across all the agents // stun.XORMappedAddress indexed by the STUN server addr xorMappedMap map[string]*xorMapped } @@ -42,7 +43,7 @@ type UniversalUDPMuxParams struct { Net transport.Net } -// NewUniversalUDPMuxDefault creates an implementation of UniversalUDPMux embedding UDPMux +// NewUniversalUDPMuxDefault creates an implementation of UniversalUDPMux embedding UDPMux. func NewUniversalUDPMuxDefault(params UniversalUDPMuxParams) *UniversalUDPMuxDefault { if params.Logger == nil { params.Logger = logging.NewDefaultLoggerFactory().NewLogger("ice") @@ -51,31 +52,31 @@ func NewUniversalUDPMuxDefault(params UniversalUDPMuxParams) *UniversalUDPMuxDef params.XORMappedAddrCacheTTL = time.Second * 25 } - m := &UniversalUDPMuxDefault{ + mux := &UniversalUDPMuxDefault{ params: params, xorMappedMap: make(map[string]*xorMapped), } // Wrap UDP connection, process server reflexive messages // before they are passed to the UDPMux connection handler (connWorker) - m.params.UDPConn = &udpConn{ + mux.params.UDPConn = &udpConn{ PacketConn: params.UDPConn, - mux: m, + mux: mux, logger: params.Logger, } // Embed UDPMux udpMuxParams := UDPMuxParams{ Logger: params.Logger, - UDPConn: m.params.UDPConn, - Net: m.params.Net, + UDPConn: mux.params.UDPConn, + Net: mux.params.Net, } - m.UDPMuxDefault = NewUDPMuxDefault(udpMuxParams) + mux.UDPMuxDefault = NewUDPMuxDefault(udpMuxParams) - return m + return mux } -// udpConn is a wrapper around UDPMux conn that overrides ReadFrom and handles STUN/TURN packets +// udpConn is a wrapper around UDPMux conn that overrides ReadFrom and handles STUN/TURN packets. type udpConn struct { net.PacketConn mux *UniversalUDPMuxDefault @@ -88,7 +89,8 @@ func (m *UniversalUDPMuxDefault) GetRelayedAddr(net.Addr, time.Duration) (*net.A return nil, errNotImplemented } -// GetConnForURL add uniques to the muxed connection by concatenating ufrag and URL (e.g. STUN URL) to be able to support multiple STUN/TURN servers +// GetConnForURL add uniques to the muxed connection by concatenating ufrag and URL +// (e.g. STUN URL) to be able to support multiple STUN/TURN servers // and return a unique connection per server. func (m *UniversalUDPMuxDefault) GetConnForURL(ufrag string, url string, addr net.Addr) (net.PacketConn, error) { return m.UDPMuxDefault.GetConn(fmt.Sprintf("%s%s", ufrag, url), addr) @@ -99,24 +101,24 @@ func (m *UniversalUDPMuxDefault) GetConnForURL(ufrag string, url string, addr ne func (c *udpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { n, addr, err = c.PacketConn.ReadFrom(p) if err != nil { - return + return n, addr, err } - if stun.IsMessage(p[:n]) { + if stun.IsMessage(p[:n]) { //nolint:nestif msg := &stun.Message{ Raw: append([]byte{}, p[:n]...), } if err = msg.Decode(); err != nil { c.logger.Warnf("Failed to handle decode ICE from %s: %v", addr.String(), err) - err = nil - return + + return n, addr, nil } udpAddr, ok := addr.(*net.UDPAddr) if !ok { // Message about this err will be logged in the UDPMux - return + return n, addr, err } if c.mux.isXORMappedResponse(msg, udpAddr.String()) { @@ -125,9 +127,11 @@ func (c *udpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { c.logger.Debugf("%w: %v", errGetXorMappedAddrResponse, err) err = nil } - return + + return n, addr, err } } + return n, addr, err } @@ -135,14 +139,16 @@ func (c *udpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { func (m *UniversalUDPMuxDefault) isXORMappedResponse(msg *stun.Message, stunAddr string) bool { m.mu.Lock() defer m.mu.Unlock() - // Check first if it is a STUN server address because remote peer can also send similar messages but as a BindingSuccess + // Check first if it is a STUN server address, + // because remote peer can also send similar messages but as a BindingSuccess. _, ok := m.xorMappedMap[stunAddr] _, err := msg.Get(stun.AttrXORMappedAddress) + return err == nil && ok } -// handleXORMappedResponse parses response from the STUN server, extracts XORMappedAddress attribute -// and set the mapped address for the server +// handleXORMappedResponse parses response from the STUN server, extracts XORMappedAddress attribute. +// and set the mapped address for the server. func (m *UniversalUDPMuxDefault) handleXORMappedResponse(stunAddr *net.UDPAddr, msg *stun.Message) error { m.mu.Lock() defer m.mu.Unlock() @@ -167,7 +173,10 @@ func (m *UniversalUDPMuxDefault) handleXORMappedResponse(stunAddr *net.UDPAddr, // Makes a STUN binding request to discover mapped address otherwise. // Blocks until the stun.XORMappedAddress has been discovered or deadline. // Method is safe for concurrent use. -func (m *UniversalUDPMuxDefault) GetXORMappedAddr(serverAddr net.Addr, deadline time.Duration) (*stun.XORMappedAddress, error) { +func (m *UniversalUDPMuxDefault) GetXORMappedAddr( + serverAddr net.Addr, + deadline time.Duration, +) (*stun.XORMappedAddress, error) { m.mu.Lock() mappedAddr, ok := m.xorMappedMap[serverAddr.String()] // If we already have a mapping for this STUN server (address already received) @@ -203,6 +212,7 @@ func (m *UniversalUDPMuxDefault) GetXORMappedAddr(serverAddr net.Addr, deadline if mappedAddr.addr == nil { return nil, errNoXorAddrMapping } + return mappedAddr.addr, nil case <-time.After(deadline): return nil, errXORMappedAddrTimeout diff --git a/udp_mux_universal_test.go b/udp_mux_universal_test.go index 3bf14ce..46f927a 100644 --- a/udp_mux_universal_test.go +++ b/udp_mux_universal_test.go @@ -43,6 +43,8 @@ func TestUniversalUDPMux(t *testing.T) { } func testMuxSrflxConnection(t *testing.T, udpMux *UniversalUDPMuxDefault, ufrag string, network string) { + t.Helper() + pktConn, err := udpMux.GetConn(ufrag, udpMux.LocalAddr()) require.NoError(t, err, "error retrieving muxed connection for ufrag") defer func() { diff --git a/udp_muxed_conn.go b/udp_muxed_conn.go index 9f438b3..e32cb30 100644 --- a/udp_muxed_conn.go +++ b/udp_muxed_conn.go @@ -28,7 +28,7 @@ type udpMuxedConnParams struct { Logger logging.LeveledLogger } -// udpMuxedConn represents a logical packet conn for a single remote as identified by ufrag +// udpMuxedConn represents a logical packet conn for a single remote as identified by ufrag. type udpMuxedConn struct { params *udpMuxedConnParams // Remote addresses that we have sent to on this conn @@ -72,11 +72,12 @@ func (c *udpMuxedConn) ReadFrom(b []byte) (n int, rAddr net.Addr, err error) { pkt.reset() c.params.AddrPool.Put(pkt) - return + return n, rAddr, err } if c.state == udpMuxedConnClosed { c.mu.Unlock() + return 0, nil, io.EOF } @@ -101,6 +102,7 @@ func (c *udpMuxedConn) WriteTo(buf []byte, rAddr net.Addr) (n int, err error) { return 0, errFailedToCastUDPAddr } + //nolint:gosec // TODO add port validation G115 ipAndPort, err := newIPPort(netUDPAddr.IP, netUDPAddr.Zone, uint16(netUDPAddr.Port)) if err != nil { return 0, err @@ -150,12 +152,14 @@ func (c *udpMuxedConn) Close() error { c.state = udpMuxedConnClosed close(c.closedChan) } + return nil } func (c *udpMuxedConn) isClosed() bool { c.mu.Lock() defer c.mu.Unlock() + return c.state == udpMuxedConnClosed } @@ -164,6 +168,7 @@ func (c *udpMuxedConn) getAddresses() []ipPort { defer c.mu.Unlock() addresses := make([]ipPort, len(c.addresses)) copy(addresses, c.addresses) + return addresses } @@ -198,6 +203,7 @@ func (c *udpMuxedConn) containsAddress(addr ipPort) bool { return true } } + return false } @@ -205,6 +211,7 @@ func (c *udpMuxedConn) writePacket(data []byte, addr *net.UDPAddr) error { pkt := c.params.AddrPool.Get().(*bufferHolder) //nolint:forcetypeassert if cap(pkt.buf) < len(data) { c.params.AddrPool.Put(pkt) + return io.ErrShortBuffer } diff --git a/url.go b/url.go index 29338bf..f18d787 100644 --- a/url.go +++ b/url.go @@ -6,77 +6,77 @@ package ice import "github.com/pion/stun/v3" type ( - // URL represents a STUN (rfc7064) or TURN (rfc7065) URI + // URL represents a STUN (rfc7064) or TURN (rfc7065) URI. // - // Deprecated: Please use pion/stun.URI + // Deprecated: Please use pion/stun.URI. URL = stun.URI // ProtoType indicates the transport protocol type that is used in the ice.URL // structure. // - // Deprecated: TPlease use pion/stun.ProtoType + // Deprecated: TPlease use pion/stun.ProtoType. ProtoType = stun.ProtoType // SchemeType indicates the type of server used in the ice.URL structure. // - // Deprecated: Please use pion/stun.SchemeType + // Deprecated: Please use pion/stun.SchemeType. SchemeType = stun.SchemeType ) const ( // SchemeTypeSTUN indicates the URL represents a STUN server. // - // Deprecated: Please use pion/stun.SchemeTypeSTUN + // Deprecated: Please use pion/stun.SchemeTypeSTUN. SchemeTypeSTUN = stun.SchemeTypeSTUN // SchemeTypeSTUNS indicates the URL represents a STUNS (secure) server. // - // Deprecated: Please use pion/stun.SchemeTypeSTUNS + // Deprecated: Please use pion/stun.SchemeTypeSTUNS. SchemeTypeSTUNS = stun.SchemeTypeSTUNS // SchemeTypeTURN indicates the URL represents a TURN server. // - // Deprecated: Please use pion/stun.SchemeTypeTURN + // Deprecated: Please use pion/stun.SchemeTypeTURN. SchemeTypeTURN = stun.SchemeTypeTURN // SchemeTypeTURNS indicates the URL represents a TURNS (secure) server. // - // Deprecated: Please use pion/stun.SchemeTypeTURNS + // Deprecated: Please use pion/stun.SchemeTypeTURNS. SchemeTypeTURNS = stun.SchemeTypeTURNS ) const ( // ProtoTypeUDP indicates the URL uses a UDP transport. // - // Deprecated: Please use pion/stun.ProtoTypeUDP + // Deprecated: Please use pion/stun.ProtoTypeUDP. ProtoTypeUDP = stun.ProtoTypeUDP // ProtoTypeTCP indicates the URL uses a TCP transport. // - // Deprecated: Please use pion/stun.ProtoTypeTCP + // Deprecated: Please use pion/stun.ProtoTypeTCP. ProtoTypeTCP = stun.ProtoTypeTCP ) -// Unknown represents and unknown ProtoType or SchemeType +// Unknown represents and unknown ProtoType or SchemeType. // -// Deprecated: Please use pion/stun.SchemeTypeUnknown or pion/stun.ProtoTypeUnknown +// Deprecated: Please use pion/stun.SchemeTypeUnknown or pion/stun.ProtoTypeUnknown. const Unknown = 0 -// ParseURL parses a STUN or TURN urls following the ABNF syntax described in +// ParseURL parses a STUN or TURN urls following the ABNF syntax described in. // https://tools.ietf.org/html/rfc7064 and https://tools.ietf.org/html/rfc7065 // respectively. // -// Deprecated: Please use pion/stun.ParseURI +// Deprecated: Please use pion/stun.ParseURI. var ParseURL = stun.ParseURI //nolint:gochecknoglobals -// NewSchemeType defines a procedure for creating a new SchemeType from a raw +// NewSchemeType defines a procedure for creating a new SchemeType from a raw. // string naming the scheme type. // -// Deprecated: Please use pion/stun.NewSchemeType +// Deprecated: Please use pion/stun.NewSchemeType. var NewSchemeType = stun.NewSchemeType //nolint:gochecknoglobals -// NewProtoType defines a procedure for creating a new ProtoType from a raw +// NewProtoType defines a procedure for creating a new ProtoType from a raw. // string naming the transport protocol type. // -// Deprecated: Please use pion/stun.NewProtoType +// Deprecated: Please use pion/stun.NewProtoType. var NewProtoType = stun.NewProtoType //nolint:gochecknoglobals diff --git a/usecandidate.go b/usecandidate.go index ea03bb8..512f504 100644 --- a/usecandidate.go +++ b/usecandidate.go @@ -11,12 +11,14 @@ type UseCandidateAttr struct{} // AddTo adds USE-CANDIDATE attribute to message. func (UseCandidateAttr) AddTo(m *stun.Message) error { m.Add(stun.AttrUseCandidate, nil) + return nil } // IsSet returns true if USE-CANDIDATE attribute is set. func (UseCandidateAttr) IsSet(m *stun.Message) bool { _, err := m.Get(stun.AttrUseCandidate) + return err == nil } diff --git a/utils_test.go b/utils_test.go index 235fda3..b16def2 100644 --- a/utils_test.go +++ b/utils_test.go @@ -13,6 +13,8 @@ import ( ) func newHostRemote(t *testing.T) *CandidateHost { + t.Helper() + remoteHostConfig := &CandidateHostConfig{ Network: "udp", Address: "1.2.3.5", @@ -21,10 +23,13 @@ func newHostRemote(t *testing.T) *CandidateHost { } hostRemote, err := NewCandidateHost(remoteHostConfig) require.NoError(t, err) + return hostRemote } func newPrflxRemote(t *testing.T) *CandidatePeerReflexive { + t.Helper() + prflxConfig := &CandidatePeerReflexiveConfig{ Network: "udp", Address: "10.10.10.2", @@ -35,10 +40,13 @@ func newPrflxRemote(t *testing.T) *CandidatePeerReflexive { } prflxRemote, err := NewCandidatePeerReflexive(prflxConfig) require.NoError(t, err) + return prflxRemote } func newSrflxRemote(t *testing.T) *CandidateServerReflexive { + t.Helper() + srflxConfig := &CandidateServerReflexiveConfig{ Network: "udp", Address: "10.10.10.2", @@ -49,10 +57,13 @@ func newSrflxRemote(t *testing.T) *CandidateServerReflexive { } srflxRemote, err := NewCandidateServerReflexive(srflxConfig) require.NoError(t, err) + return srflxRemote } func newRelayRemote(t *testing.T) *CandidateRelay { + t.Helper() + relayConfig := &CandidateRelayConfig{ Network: "udp", Address: "1.2.3.4", @@ -63,10 +74,13 @@ func newRelayRemote(t *testing.T) *CandidateRelay { } relayRemote, err := NewCandidateRelay(relayConfig) require.NoError(t, err) + return relayRemote } func newHostLocal(t *testing.T) *CandidateHost { + t.Helper() + localHostConfig := &CandidateHostConfig{ Network: "udp", Address: "192.168.1.1", @@ -75,5 +89,6 @@ func newHostLocal(t *testing.T) *CandidateHost { } hostLocal, err := NewCandidateHost(localHostConfig) require.NoError(t, err) + return hostLocal } From 47dad556f13a115afbc3120d565f6e4e09e3deec Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 30 Jan 2025 00:17:06 -0600 Subject: [PATCH 090/114] Add methods to add and remove extensions Added `AddExtension` and `RemoveExtension` methods to `ICECandidate`, allowing extensions to be managed dynamically. Ensure that `TCPType` is stored in one place (candidate.TCPType) --- candidate.go | 8 ++- candidate_base.go | 76 +++++++++++++++++++--- candidate_test.go | 156 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 229 insertions(+), 11 deletions(-) diff --git a/candidate.go b/candidate.go index 4eb5206..89082f9 100644 --- a/candidate.go +++ b/candidate.go @@ -58,12 +58,18 @@ type Candidate interface { // https://datatracker.ietf.org/doc/html/rfc5245#section-15.1 //. Extensions() []CandidateExtension - // GetExtension returns the value of the extension attribute associated with the ICECandidate. // Extension attributes are defined in RFC 5245, Section 15.1: // https://datatracker.ietf.org/doc/html/rfc5245#section-15.1 //. GetExtension(key string) (value CandidateExtension, ok bool) + // AddExtension adds an extension attribute to the ICECandidate. + // If an extension with the same key already exists, it will be overwritten. + // Extension attributes are defined in RFC 5245, Section 15.1: + AddExtension(extension CandidateExtension) error + // RemoveExtension removes an extension attribute from the ICECandidate. + // Extension attributes are defined in RFC 5245, Section 15.1: + RemoveExtension(key string) (ok bool) String() string Type() CandidateType diff --git a/candidate_base.go b/candidate_base.go index 55a6ce8..f2ef422 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -548,17 +548,22 @@ type CandidateExtension struct { } func (c *candidateBase) Extensions() []CandidateExtension { - // IF Extensions were not parsed using UnmarshalCandidate - // For backwards compatibility when the TCPType is set manually - if len(c.extensions) == 0 && c.TCPType() != TCPTypeUnspecified { - return []CandidateExtension{{ - Key: "tcptype", - Value: c.TCPType().String(), - }} + tcpType := c.TCPType() + hasTCPType := 0 + if tcpType != TCPTypeUnspecified { + hasTCPType = 1 } - extensions := make([]CandidateExtension, len(c.extensions)) - copy(extensions, c.extensions) + extensions := make([]CandidateExtension, len(c.extensions)+hasTCPType) + // We store the TCPType in c.tcpType, but we need to return it as an extension. + if hasTCPType == 1 { + extensions[0] = CandidateExtension{ + Key: "tcptype", + Value: tcpType.String(), + } + } + + copy(extensions[hasTCPType:], c.extensions) return extensions } @@ -576,7 +581,7 @@ func (c *candidateBase) GetExtension(key string) (CandidateExtension, bool) { } // TCPType was manually set. - if key == "tcptype" && c.TCPType() != TCPTypeUnspecified { + if key == "tcptype" && c.TCPType() != TCPTypeUnspecified { //nolint:goconst extension.Value = c.TCPType().String() return extension, true @@ -585,6 +590,55 @@ func (c *candidateBase) GetExtension(key string) (CandidateExtension, bool) { return extension, false } +func (c *candidateBase) AddExtension(ext CandidateExtension) error { + if ext.Key == "tcptype" { + tcpType := NewTCPType(ext.Value) + if tcpType == TCPTypeUnspecified { + return fmt.Errorf("%w: invalid or unsupported TCPtype %s", errParseTCPType, ext.Value) + } + + c.tcpType = tcpType + + return nil + } + + if ext.Key == "" { + return fmt.Errorf("%w: key is empty", errParseExtension) + } + + // per spec, Extensions aren't explicitly unique, we only set the first one. + // If the exteion is set multiple times. + for i := range c.extensions { + if c.extensions[i].Key == ext.Key { + c.extensions[i] = ext + + return nil + } + } + + c.extensions = append(c.extensions, ext) + + return nil +} + +func (c *candidateBase) RemoveExtension(key string) (ok bool) { + if key == "tcptype" { + c.tcpType = TCPTypeUnspecified + ok = true + } + + for i := range c.extensions { + if c.extensions[i].Key == key { + c.extensions = append(c.extensions[:i], c.extensions[i+1:]...) + ok = true + + break + } + } + + return ok +} + // marshalExtensions returns the string representation of the candidate extensions. func (c *candidateBase) marshalExtensions() string { value := "" @@ -994,6 +1048,8 @@ func unmarshalCandidateExtensions(raw string) (extensions []CandidateExtension, if key == "tcptype" { rawTCPTypeRaw = value + + continue } extensions = append(extensions, CandidateExtension{key, value}) diff --git a/candidate_test.go b/candidate_test.go index caafefe..514a9de 100644 --- a/candidate_test.go +++ b/candidate_test.go @@ -1271,3 +1271,159 @@ func TestBaseCandidateExtensionsEqual(t *testing.T) { }) } } + +func TestCandidateAddExtension(t *testing.T) { + t.Run("Add extension", func(t *testing.T) { + candidate, err := NewCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }) + if err != nil { + t.Error(err) + } + + require.NoError(t, candidate.AddExtension(CandidateExtension{"a", "b"})) + require.NoError(t, candidate.AddExtension(CandidateExtension{"c", "d"})) + + extensions := candidate.Extensions() + require.Equal(t, []CandidateExtension{{"a", "b"}, {"c", "d"}}, extensions) + }) + + t.Run("Add extension with existing key", func(t *testing.T) { + candidate, err := NewCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }) + if err != nil { + t.Error(err) + } + + require.NoError(t, candidate.AddExtension(CandidateExtension{"a", "b"})) + require.NoError(t, candidate.AddExtension(CandidateExtension{"a", "d"})) + + extensions := candidate.Extensions() + require.Equal(t, []CandidateExtension{{"a", "d"}}, extensions) + }) + + t.Run("Keep tcptype extension", func(t *testing.T) { + candidate, err := NewCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeTCP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + TCPType: TCPTypeActive, + }) + if err != nil { + t.Error(err) + } + + ext, ok := candidate.GetExtension("tcptype") + require.True(t, ok) + require.Equal(t, ext, CandidateExtension{"tcptype", "active"}) + require.Equal(t, candidate.Extensions(), []CandidateExtension{{"tcptype", "active"}}) + + require.NoError(t, candidate.AddExtension(CandidateExtension{"a", "b"})) + + ext, ok = candidate.GetExtension("tcptype") + require.True(t, ok) + require.Equal(t, ext, CandidateExtension{"tcptype", "active"}) + require.Equal(t, candidate.Extensions(), []CandidateExtension{{"tcptype", "active"}, {"a", "b"}}) + }) + + t.Run("TcpType change extension", func(t *testing.T) { + candidate, err := NewCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeTCP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }) + if err != nil { + t.Error(err) + } + + require.NoError(t, candidate.AddExtension(CandidateExtension{"tcptype", "active"})) + + extensions := candidate.Extensions() + require.Equal(t, []CandidateExtension{{"tcptype", "active"}}, extensions) + require.Equal(t, TCPTypeActive, candidate.TCPType()) + + require.Error(t, candidate.AddExtension(CandidateExtension{"tcptype", "INVALID"})) + }) +} + +func TestCandidateRemoveExtension(t *testing.T) { + t.Run("Remove extension", func(t *testing.T) { + candidate, err := NewCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }) + if err != nil { + t.Error(err) + } + + require.NoError(t, candidate.AddExtension(CandidateExtension{"a", "b"})) + require.NoError(t, candidate.AddExtension(CandidateExtension{"c", "d"})) + + require.True(t, candidate.RemoveExtension("a")) + + extensions := candidate.Extensions() + require.Equal(t, []CandidateExtension{{"c", "d"}}, extensions) + }) + + t.Run("Remove extension that does not exist", func(t *testing.T) { + candidate, err := NewCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }) + if err != nil { + t.Error(err) + } + + require.NoError(t, candidate.AddExtension(CandidateExtension{"a", "b"})) + require.NoError(t, candidate.AddExtension(CandidateExtension{"c", "d"})) + + require.False(t, candidate.RemoveExtension("b")) + + extensions := candidate.Extensions() + require.Equal(t, []CandidateExtension{{"a", "b"}, {"c", "d"}}, extensions) + }) + + t.Run("Remove tcptype extension", func(t *testing.T) { + candidate, err := NewCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeTCP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + TCPType: TCPTypeActive, + }) + if err != nil { + t.Error(err) + } + + // tcptype extension should be removed, even if it's not in the extensions list (Not Parsed) + require.True(t, candidate.RemoveExtension("tcptype")) + require.Equal(t, TCPTypeUnspecified, candidate.TCPType()) + require.Empty(t, candidate.Extensions()) + + require.NoError(t, candidate.AddExtension(CandidateExtension{"tcptype", "passive"})) + + require.True(t, candidate.RemoveExtension("tcptype")) + require.Equal(t, TCPTypeUnspecified, candidate.TCPType()) + require.Empty(t, candidate.Extensions()) + }) +} From 9dfb5c26676a7f76fe9eafab5246cfb910d2d6e3 Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 30 Jan 2025 22:30:30 -0600 Subject: [PATCH 091/114] Allow for empty extension values While not spec compliant, some implementations allow for empty extension values. This aligns with our behavior for empty foundation values. And makes the parser more forgiving for bad implementations. --- candidate_base.go | 22 ++++++++++------------ candidate_test.go | 47 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/candidate_base.go b/candidate_base.go index f2ef422..e7660d6 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -1032,20 +1032,18 @@ func unmarshalCandidateExtensions(raw string) (extensions []CandidateExtension, } i = next - if i >= len(raw) { - return extensions, "", fmt.Errorf( - "%w: missing value for %s in %s", errParseExtension, key, raw, //nolint: errorlint // we are wrapping the error - ) + // while not spec-compliant, we allow for empty values, as seen in the wild + var value string + if i < len(raw) { + value, next, err = readCandidateByteString(raw, i) + if err != nil { + return extensions, "", fmt.Errorf( + "%w: failed to read value %v", errParseExtension, err, //nolint: errorlint // we are wrapping the error + ) + } + i = next } - value, next, err := readCandidateByteString(raw, i) - if err != nil { - return extensions, "", fmt.Errorf( - "%w: failed to read value %v", errParseExtension, err, //nolint: errorlint // we are wrapping the error - ) - } - i = next - if key == "tcptype" { rawTCPTypeRaw = value diff --git a/candidate_test.go b/candidate_test.go index 514a9de..4694cec 100644 --- a/candidate_test.go +++ b/candidate_test.go @@ -698,8 +698,20 @@ func TestCandidateExtensionsMarshal(t *testing.T) { "1052353102 1 tcp 2128609279 192.168.0.196 0 typ host", }, { - []CandidateExtension{}, - "1052353102 1 tcp 2128609279 192.168.0.196 0 typ host", + []CandidateExtension{ + {"tcptype", "active"}, + {"empty-value-1", ""}, + {"empty-value-2", ""}, + }, + "1052353102 1 tcp 2128609279 192.168.0.196 0 typ host tcptype active empty-value-1 empty-value-2", + }, + { + []CandidateExtension{ + {"tcptype", "active"}, + {"empty-value-1", ""}, + {"empty-value-2", ""}, + }, + "1052353102 1 tcp 2128609279 192.168.0.196 0 typ host tcptype active empty-value-1 empty-value-2 ", }, } @@ -967,16 +979,10 @@ func TestUnmarshalCandidateExtensions(t *testing.T) { }, fail: false, }, - { - name: "invalid extension string", - value: "invalid", - expected: []CandidateExtension{}, - fail: true, - }, { name: "invalid extension", - value: " a b", - expected: []CandidateExtension{{"a", "b"}, {"c", "d"}}, + value: " a b d", + expected: []CandidateExtension{{"", "a"}, {"b", "d"}}, fail: true, }, } @@ -1357,6 +1363,27 @@ func TestCandidateAddExtension(t *testing.T) { require.Error(t, candidate.AddExtension(CandidateExtension{"tcptype", "INVALID"})) }) + + t.Run("Add empty extension", func(t *testing.T) { + candidate, err := NewCandidateHost(&CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: "fcd9:e3b8:12ce:9fc5:74a5:c6bb:d8b:e08a", + Port: 53987, + Priority: 500, + Foundation: "750", + }) + if err != nil { + t.Error(err) + } + + require.Error(t, candidate.AddExtension(CandidateExtension{"", ""})) + + require.NoError(t, candidate.AddExtension(CandidateExtension{"a", ""})) + + extensions := candidate.Extensions() + + require.Equal(t, []CandidateExtension{{"a", ""}}, extensions) + }) } func TestCandidateRemoveExtension(t *testing.T) { From 141df5a086e3163106b3d721584ff8a7322b7b03 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 31 Jan 2025 05:33:45 +0000 Subject: [PATCH 092/114] Update module github.com/pion/logging to v0.2.3 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8f3abd4..d42aa67 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/google/uuid v1.6.0 github.com/pion/dtls/v3 v3.0.4 - github.com/pion/logging v0.2.2 + github.com/pion/logging v0.2.3 github.com/pion/mdns/v2 v2.0.7 github.com/pion/randutil v0.1.0 github.com/pion/stun/v3 v3.0.0 diff --git a/go.sum b/go.sum index dbe6197..5431b28 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U= github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg= -github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= -github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= +github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90= github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= From d21ae5e0e54465db200d27e6c61d2f44eb012aad Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 30 Jan 2025 01:14:01 -0600 Subject: [PATCH 093/114] Include ufrag in generated ICE candidates Include ufrag extension in the ICE candidates generated by the ICE agent --- agent.go | 11 +++++++++++ agent_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/agent.go b/agent.go index 4ce1b1f..fd1faae 100644 --- a/agent.go +++ b/agent.go @@ -799,6 +799,7 @@ func (a *Agent) addCandidate(ctx context.Context, cand Candidate, candidateConn } } + a.setCandidateExtensions(cand) cand.start(a, candidateConn, a.startedCh) set = append(set, cand) @@ -818,6 +819,16 @@ func (a *Agent) addCandidate(ctx context.Context, cand Candidate, candidateConn }) } +func (a *Agent) setCandidateExtensions(cand Candidate) { + err := cand.AddExtension(CandidateExtension{ + Key: "ufrag", + Value: a.localUfrag, + }) + if err != nil { + a.log.Errorf("Failed to add ufrag extension to candidate: %v", err) + } +} + // GetRemoteCandidates returns the remote candidates. func (a *Agent) GetRemoteCandidates() ([]Candidate, error) { var res []Candidate diff --git a/agent_test.go b/agent_test.go index f1d7f36..42a1846 100644 --- a/agent_test.go +++ b/agent_test.go @@ -2071,3 +2071,42 @@ func TestAgentGracefulCloseDeadlock(t *testing.T) { closeNow.Done() closed.Wait() } + +func TestSetCandidatesUfrag(t *testing.T) { + var config AgentConfig + + agent, err := NewAgent(&config) + if err != nil { + t.Fatalf("Error constructing ice.Agent: %v", err) + } + defer func() { + require.NoError(t, agent.Close()) + }() + + dummyConn := &net.UDPConn{} + + for i := 0; i < 5; i++ { + cfg := CandidateHostConfig{ + Network: "udp", + Address: "192.168.0.2", + Port: 1000 + i, + Component: 1, + } + + cand, errCand := NewCandidateHost(&cfg) + require.NoError(t, errCand) + + err = agent.addCandidate(context.Background(), cand, dummyConn) + require.NoError(t, err) + } + + actualCandidates, err := agent.GetLocalCandidates() + require.NoError(t, err) + + for _, candidate := range actualCandidates { + ext, ok := candidate.GetExtension("ufrag") + + require.True(t, ok) + require.Equal(t, agent.localUfrag, ext.Value) + } +} From f92d05f17c76e8ce326bad6cd9002f383b8d1415 Mon Sep 17 00:00:00 2001 From: cnderrauber Date: Thu, 27 Feb 2025 12:57:32 +0800 Subject: [PATCH 094/114] Add Req/Res count/time to candidate stats (#763) These details will provide information for connectivity issue. --- agent.go | 18 ++++++-- agent_stats.go | 16 ++++--- agent_test.go | 17 ++++++-- candidatepair.go | 110 ++++++++++++++++++++++++++++++++++++++++++++++- selection.go | 5 ++- stats.go | 12 ++++++ 6 files changed, 163 insertions(+), 15 deletions(-) diff --git a/agent.go b/agent.go index fd1faae..7110c32 100644 --- a/agent.go +++ b/agent.go @@ -980,18 +980,23 @@ func (a *Agent) findRemoteCandidate(networkType NetworkType, addr net.Addr) Cand return nil } -func (a *Agent) sendBindingRequest(m *stun.Message, local, remote Candidate) { +func (a *Agent) sendBindingRequest(msg *stun.Message, local, remote Candidate) { a.log.Tracef("Ping STUN from %s to %s", local, remote) a.invalidatePendingBindingRequests(time.Now()) a.pendingBindingRequests = append(a.pendingBindingRequests, bindingRequest{ timestamp: time.Now(), - transactionID: m.TransactionID, + transactionID: msg.TransactionID, destination: remote.addr(), - isUseCandidate: m.Contains(stun.AttrUseCandidate), + isUseCandidate: msg.Contains(stun.AttrUseCandidate), }) - a.sendSTUN(m, local, remote) + if pair := a.findPair(local, remote); pair != nil { + pair.UpdateRequestSent() + } else { + a.log.Warnf("Failed to find pair for add binding request from %s to %s", local, remote) + } + a.sendSTUN(msg, local, remote) } func (a *Agent) sendBindingSuccess(m *stun.Message, local, remote Candidate) { @@ -1014,6 +1019,11 @@ func (a *Agent) sendBindingSuccess(m *stun.Message, local, remote Candidate) { ); err != nil { a.log.Warnf("Failed to handle inbound ICE from: %s to: %s error: %s", local, remote, err) } else { + if pair := a.findPair(local, remote); pair != nil { + pair.UpdateResponseSent() + } else { + a.log.Warnf("Failed to find pair for add binding response from %s to %s", local, remote) + } a.sendSTUN(out, local, remote) } } diff --git a/agent_stats.go b/agent_stats.go index 45a5629..c6b21f5 100644 --- a/agent_stats.go +++ b/agent_stats.go @@ -26,18 +26,22 @@ func (a *Agent) GetCandidatePairsStats() []CandidatePairStats { // BytesReceived uint64 // LastPacketSentTimestamp time.Time // LastPacketReceivedTimestamp time.Time - // FirstRequestTimestamp time.Time - // LastRequestTimestamp time.Time - // LastResponseTimestamp time.Time + FirstRequestTimestamp: cp.FirstRequestSentAt(), + LastRequestTimestamp: cp.LastRequestSentAt(), + FirstResponseTimestamp: cp.FirstReponseReceivedAt(), + LastResponseTimestamp: cp.LastResponseReceivedAt(), + FirstRequestReceivedTimestamp: cp.FirstRequestReceivedAt(), + LastRequestReceivedTimestamp: cp.LastRequestReceivedAt(), + TotalRoundTripTime: cp.TotalRoundTripTime(), CurrentRoundTripTime: cp.CurrentRoundTripTime(), // AvailableOutgoingBitrate float64 // AvailableIncomingBitrate float64 // CircuitBreakerTriggerCount uint32 - // RequestsReceived uint64 - // RequestsSent uint64 + RequestsReceived: cp.RequestsReceived(), + RequestsSent: cp.RequestsSent(), ResponsesReceived: cp.ResponsesReceived(), - // ResponsesSent uint64 + ResponsesSent: cp.ResponsesSent(), // RetransmissionsReceived uint64 // RetransmissionsSent uint64 // ConsentRequestsSent uint64 diff --git a/agent_test.go b/agent_test.go index 42a1846..8fd289a 100644 --- a/agent_test.go +++ b/agent_test.go @@ -636,7 +636,7 @@ func TestInvalidGather(t *testing.T) { }) } -func TestCandidatePairsStats(t *testing.T) { //nolint:cyclop +func TestCandidatePairsStats(t *testing.T) { //nolint:cyclop,gocyclo defer test.CheckRoutines(t)() // Avoid deadlocks? @@ -715,14 +715,18 @@ func TestCandidatePairsStats(t *testing.T) { //nolint:cyclop p := agent.findPair(hostLocal, remote) if p == nil { - agent.addPair(hostLocal, remote) + p = agent.addPair(hostLocal, remote) } + p.UpdateRequestReceived() + p.UpdateRequestSent() + p.UpdateResponseSent() + p.UpdateRoundTripTime(time.Second) } p := agent.findPair(hostLocal, prflxRemote) p.state = CandidatePairStateFailed - for i := 0; i < 10; i++ { + for i := 1; i < 10; i++ { p.UpdateRoundTripTime(time.Duration(i+1) * time.Second) } @@ -749,6 +753,13 @@ func TestCandidatePairsStats(t *testing.T) { //nolint:cyclop default: t.Fatal("invalid remote candidate ID") } + + if cps.FirstRequestTimestamp.IsZero() || cps.LastRequestTimestamp.IsZero() || + cps.FirstResponseTimestamp.IsZero() || cps.LastResponseTimestamp.IsZero() || + cps.FirstRequestReceivedTimestamp.IsZero() || cps.LastRequestReceivedTimestamp.IsZero() || + cps.RequestsReceived == 0 || cps.RequestsSent == 0 || cps.ResponsesSent == 0 || cps.ResponsesReceived == 0 { + t.Fatal("failed to verify pair stats counter and timestamps", cps) + } } if relayPairStat.RemoteCandidateID != relayRemote.ID() { diff --git a/candidatepair.go b/candidatepair.go index e2b4d5e..82655aa 100644 --- a/candidatepair.go +++ b/candidatepair.go @@ -33,7 +33,18 @@ type CandidatePair struct { // stats currentRoundTripTime int64 // in ns totalRoundTripTime int64 // in ns - responsesReceived uint64 + + requestsReceived uint64 + requestsSent uint64 + responsesReceived uint64 + responsesSent uint64 + + firstRequestSentAt atomic.Value // time.Time + lastRequestSentAt atomic.Value // time.Time + firstReponseReceivedAt atomic.Value // time.Time + lastResponseReceivedAt atomic.Value // time.Time + firstRequestReceivedAt atomic.Value // time.Time + lastRequestReceivedAt atomic.Value // time.Time } func (p *CandidatePair) String() string { @@ -127,6 +138,10 @@ func (p *CandidatePair) UpdateRoundTripTime(rtt time.Duration) { atomic.StoreInt64(&p.currentRoundTripTime, rttNs) atomic.AddInt64(&p.totalRoundTripTime, rttNs) atomic.AddUint64(&p.responsesReceived, 1) + + now := time.Now() + p.firstReponseReceivedAt.CompareAndSwap(nil, now) + p.lastResponseReceivedAt.Store(now) } // CurrentRoundTripTime returns the current round trip time in seconds @@ -141,8 +156,101 @@ func (p *CandidatePair) TotalRoundTripTime() float64 { return time.Duration(atomic.LoadInt64(&p.totalRoundTripTime)).Seconds() } +// RequestsReceived returns the total number of connectivity checks received +// https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-requestsreceived +func (p *CandidatePair) RequestsReceived() uint64 { + return atomic.LoadUint64(&p.requestsReceived) +} + +// RequestsSent returns the total number of connectivity checks sent +// https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-requestssent +func (p *CandidatePair) RequestsSent() uint64 { + return atomic.LoadUint64(&p.requestsSent) +} + // ResponsesReceived returns the total number of connectivity responses received // https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-responsesreceived func (p *CandidatePair) ResponsesReceived() uint64 { return atomic.LoadUint64(&p.responsesReceived) } + +// ResponsesSent returns the total number of connectivity responses sent +// https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-responsessent +func (p *CandidatePair) ResponsesSent() uint64 { + return atomic.LoadUint64(&p.responsesSent) +} + +// FirstRequestSentAt returns the timestamp of the first connectivity check sent. +func (p *CandidatePair) FirstRequestSentAt() time.Time { + if v, ok := p.firstRequestSentAt.Load().(time.Time); ok { + return v + } + + return time.Time{} +} + +// LastRequestSentAt returns the timestamp of the last connectivity check sent. +func (p *CandidatePair) LastRequestSentAt() time.Time { + if v, ok := p.lastRequestSentAt.Load().(time.Time); ok { + return v + } + + return time.Time{} +} + +// FirstReponseReceivedAt returns the timestamp of the first connectivity response received. +func (p *CandidatePair) FirstReponseReceivedAt() time.Time { + if v, ok := p.firstReponseReceivedAt.Load().(time.Time); ok { + return v + } + + return time.Time{} +} + +// LastResponseReceivedAt returns the timestamp of the last connectivity response received. +func (p *CandidatePair) LastResponseReceivedAt() time.Time { + if v, ok := p.lastResponseReceivedAt.Load().(time.Time); ok { + return v + } + + return time.Time{} +} + +// FirstRequestReceivedAt returns the timestamp of the first connectivity check received. +func (p *CandidatePair) FirstRequestReceivedAt() time.Time { + if v, ok := p.firstRequestReceivedAt.Load().(time.Time); ok { + return v + } + + return time.Time{} +} + +// LastRequestReceivedAt returns the timestamp of the last connectivity check received. +func (p *CandidatePair) LastRequestReceivedAt() time.Time { + if v, ok := p.lastRequestReceivedAt.Load().(time.Time); ok { + return v + } + + return time.Time{} +} + +// UpdateRequestSent increments the number of requests sent and updates the timestamp. +func (p *CandidatePair) UpdateRequestSent() { + atomic.AddUint64(&p.requestsSent, 1) + now := time.Now() + p.firstRequestSentAt.CompareAndSwap(nil, now) + p.lastRequestSentAt.Store(now) +} + +// UpdateResponseSent increments the number of responses sent. +func (p *CandidatePair) UpdateResponseSent() { + atomic.AddUint64(&p.responsesSent, 1) +} + +// UpdateRequestReceived increments the number of requests received and updates the timestamp. +func (p *CandidatePair) UpdateRequestReceived() { + atomic.AddUint64(&p.requestsReceived, 1) + now := time.Now() + p.firstRequestReceivedAt.CompareAndSwap(nil, now) + p.lastRequestReceivedAt.Store(now) +} diff --git a/selection.go b/selection.go index c5fce39..b67898d 100644 --- a/selection.go +++ b/selection.go @@ -100,10 +100,12 @@ func (s *controllingSelector) HandleBindingRequest(message *stun.Message, local, pair := s.agent.findPair(local, remote) if pair == nil { - s.agent.addPair(local, remote) + pair = s.agent.addPair(local, remote) + pair.UpdateRequestReceived() return } + pair.UpdateRequestReceived() if pair.state == CandidatePairStateSucceeded && s.nominatedPair == nil && s.agent.getSelectedPair() == nil { bestPair := s.agent.getBestAvailableCandidatePair() @@ -281,6 +283,7 @@ func (s *controlledSelector) HandleBindingRequest(message *stun.Message, local, if pair == nil { pair = s.agent.addPair(local, remote) } + pair.UpdateRequestReceived() if message.Contains(stun.AttrUseCandidate) { //nolint:nestif // https://tools.ietf.org/html/rfc8445#section-7.3.1.5 diff --git a/stats.go b/stats.go index 8a08f67..30a0c02 100644 --- a/stats.go +++ b/stats.go @@ -58,10 +58,22 @@ type CandidatePairStats struct { // (LastRequestTimestamp - FirstRequestTimestamp) / RequestsSent. LastRequestTimestamp time.Time + // FirstResponseTimestamp represents the timestamp at which the first STUN response + // was received on this particular candidate pair. + FirstResponseTimestamp time.Time + // LastResponseTimestamp represents the timestamp at which the last STUN response // was received on this particular candidate pair. LastResponseTimestamp time.Time + // FirstRequestReceivedTimestamp represents the timestamp at which the first + // connectivity check request was received. + FirstRequestReceivedTimestamp time.Time + + // LastRequestReceivedTimestamp represents the timestamp at which the last + // connectivity check request was received. + LastRequestReceivedTimestamp time.Time + // TotalRoundTripTime represents the sum of all round trip time measurements // in seconds since the beginning of the session, based on STUN connectivity // check responses (ResponsesReceived), including those that reply to requests From 37fb5d2fc34dd7df9d09fe4d9fc2541e80a1b675 Mon Sep 17 00:00:00 2001 From: oto313 Date: Mon, 17 Mar 2025 19:06:54 +0100 Subject: [PATCH 095/114] Always send KeepAlives (#767) Pion incorrectly resets the consent timer when sending any traffic. The consent timer must only be reset on STUN traffic. RFC 7675 > Consent expires after 30 seconds. That is, if a valid STUN binding > response has not been received from the remote peer's transport > address in 30 seconds, the endpoint MUST cease transmission on that > 5-tuple. STUN consent responses received after consent expiry do not > re-establish consent and may be discarded or cause an ICMP error. --- agent.go | 4 +--- agent_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/agent.go b/agent.go index 7110c32..87949b1 100644 --- a/agent.go +++ b/agent.go @@ -594,9 +594,7 @@ func (a *Agent) checkKeepalive() { return } - if (a.keepaliveInterval != 0) && - ((time.Since(selectedPair.Local.LastSent()) > a.keepaliveInterval) || - (time.Since(selectedPair.Remote.LastReceived()) > a.keepaliveInterval)) { + if a.keepaliveInterval != 0 { // We use binding request instead of indication to support refresh consent schemas // see https://tools.ietf.org/html/rfc7675 a.selector.PingCandidate(selectedPair.Local, selectedPair.Remote) diff --git a/agent_test.go b/agent_test.go index 8fd289a..1ec75c1 100644 --- a/agent_test.go +++ b/agent_test.go @@ -2121,3 +2121,42 @@ func TestSetCandidatesUfrag(t *testing.T) { require.Equal(t, agent.localUfrag, ext.Value) } } + +func TestAlwaysSentKeepAlive(t *testing.T) { //nolint:cyclop + defer test.CheckRoutines(t)() + + // Avoid deadlocks? + defer test.TimeOut(1 * time.Second).Stop() + + agent, err := NewAgent(&AgentConfig{}) + if err != nil { + t.Fatalf("Failed to create agent: %s", err) + } + defer func() { + require.NoError(t, agent.Close()) + }() + + log := logging.NewDefaultLoggerFactory().NewLogger("agent") + agent.selector = &controllingSelector{agent: agent, log: log} + pair := makeCandidatePair(t) + if s, ok := pair.Local.(*CandidateHost); ok { + s.conn = &fakenet.MockPacketConn{} + } else { + t.Fatalf("Invalid local candidate") + } + agent.setSelectedPair(pair) + + pair.Remote.seen(false) + + lastSent := pair.Local.LastSent() + agent.checkKeepalive() + newLastSent := pair.Local.LastSent() + require.NotEqual(t, lastSent, newLastSent) + lastSent = newLastSent + + // sleep, so there is difference in sent time of local candidate + time.Sleep(10 * time.Millisecond) + agent.checkKeepalive() + newLastSent = pair.Local.LastSent() + require.NotEqual(t, lastSent, newLastSent) +} From ef453b3fdd68bee1c36bdd489bcc72e810b9f682 Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Tue, 25 Mar 2025 06:27:32 +0200 Subject: [PATCH 096/114] Handle candidate: prefix with UnmarshalCandidate Make UnmarshalCandidate able to handle candidate: prefix in the candidate string. --- candidate_base.go | 4 +++- candidate_test.go | 31 ++++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/candidate_base.go b/candidate_base.go index e7660d6..66fb776 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -693,8 +693,10 @@ func (c *candidateBase) setExtensions(extensions []CandidateExtension) { // UnmarshalCandidate Parses a candidate from a string // https://datatracker.ietf.org/doc/html/rfc5245#section-15.1 func UnmarshalCandidate(raw string) (Candidate, error) { //nolint:cyclop - pos := 0 + // Handle candidates with the "candidate:" prefix as defined in RFC 5245 section 15.1. + raw = strings.TrimPrefix(raw, "candidate:") + pos := 0 // foundation ( 1*32ice-char ) But we allow for empty foundation, foundation, pos, err := readCandidateCharToken(raw, pos, 32) if err != nil { diff --git a/candidate_test.go b/candidate_test.go index 4694cec..ac7b642 100644 --- a/candidate_test.go +++ b/candidate_test.go @@ -6,6 +6,7 @@ package ice import ( "net" "strconv" + "strings" "testing" "time" @@ -465,6 +466,18 @@ func TestCandidateMarshal(t *testing.T) { " 1 udp 500 " + localhostIPStr + " 80 typ host", false, }, + // Missing Foundation + { + mustCandidateHost(t, &CandidateHostConfig{ + Network: NetworkTypeUDP4.String(), + Address: localhostIPStr, + Port: 80, + Priority: 500, + Foundation: " ", + }), + "candidate: 1 udp 500 " + localhostIPStr + " 80 typ host", + false, + }, { mustCandidateHost(t, &CandidateHostConfig{ Network: NetworkTypeUDP4.String(), @@ -487,6 +500,17 @@ func TestCandidateMarshal(t *testing.T) { "3359356140 1 tcp 1671430143 172.28.142.173 7686 typ host", false, }, + { + mustCandidateHost(t, &CandidateHostConfig{ + Network: NetworkTypeTCP4.String(), + Address: "172.28.142.173", + Port: 7686, + Priority: 1671430143, + Foundation: "+/3713fhi", + }), + "candidate:3359356140 1 tcp 1671430143 172.28.142.173 7686 typ host", + false, + }, // Invalid candidates {nil, "", true}, @@ -562,7 +586,12 @@ func TestCandidateMarshal(t *testing.T) { test.candidate.String(), actualCandidate.String(), ) - require.Equal(t, test.marshaled, actualCandidate.Marshal()) + + if strings.HasPrefix(test.marshaled, "candidate:") { + require.Equal(t, test.marshaled[len("candidate:"):], actualCandidate.Marshal()) + } else { + require.Equal(t, test.marshaled, actualCandidate.Marshal()) + } }) } } From dddf6a45659e73e37018c07e3935ca20c40f3e69 Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Mon, 7 Apr 2025 05:29:52 +0200 Subject: [PATCH 097/114] Update social media links, move to discord --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 14e89ad..5171a41 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@

A Go implementation of ICE

Pion ICE - Slack Widget + join us on Discord Follow us on Bluesky
GitHub Workflow Status Go Reference @@ -20,9 +20,9 @@ The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones. ### Community -Pion has an active community on the [Slack](https://pion.ly/slack). +Pion has an active community on the [Discord](https://discord.gg/PngbdqpFbt). -Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news. +Follow the [Pion Bluesky](https://bsky.app/profile/pion.ly) or [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news. We are always looking to support **your projects**. Please reach out if you have something to build! If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly) From dd072edae989654ad76791ddb5fbd4b81b1ffdab Mon Sep 17 00:00:00 2001 From: sirzooro Date: Sat, 12 Apr 2025 19:09:54 +0200 Subject: [PATCH 098/114] Pass LoggerFactory to dtls and mdns (#772) --- agent.go | 1 + gather.go | 1 + mdns.go | 3 +++ 3 files changed, 5 insertions(+) diff --git a/agent.go b/agent.go index 87949b1..80c89d0 100644 --- a/agent.go +++ b/agent.go @@ -267,6 +267,7 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit,cyclop mDNSMode, mDNSName, log, + loggerFactory, ); err != nil { log.Warnf("Failed to initialize mDNS %s: %v", mDNSName, err) } diff --git a/gather.go b/gather.go index 9d6bda0..a1e0247 100644 --- a/gather.go +++ b/gather.go @@ -758,6 +758,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { conn, connectErr := dtls.Client(&fakenet.PacketConn{Conn: udpConn}, udpConn.RemoteAddr(), &dtls.Config{ ServerName: url.Host, InsecureSkipVerify: a.insecureSkipVerify, //nolint:gosec + LoggerFactory: a.loggerFactory, }) if connectErr != nil { a.log.Warnf("Failed to create DTLS client: %v", turnServerAddr, connectErr) diff --git a/mdns.go b/mdns.go index ac0756d..b88909d 100644 --- a/mdns.go +++ b/mdns.go @@ -47,6 +47,7 @@ func createMulticastDNS( mDNSMode MulticastDNSMode, mDNSName string, log logging.LeveledLogger, + loggerFactory logging.LoggerFactory, ) (*mdns.Conn, MulticastDNSMode, error) { if mDNSMode == MulticastDNSModeDisabled { return nil, mDNSMode, nil @@ -124,6 +125,7 @@ func createMulticastDNS( conn, err := mdns.Server(pktConnV4, pktConnV6, &mdns.Config{ Interfaces: ifcs, IncludeLoopback: includeLoopback, + LoggerFactory: loggerFactory, }) return conn, mDNSMode, err @@ -132,6 +134,7 @@ func createMulticastDNS( Interfaces: ifcs, IncludeLoopback: includeLoopback, LocalNames: []string{mDNSName}, + LoggerFactory: loggerFactory, }) return conn, mDNSMode, err From f32c107a626ceb542aaefb5082dacd8cfc360e05 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Tue, 22 Apr 2025 13:44:26 -0400 Subject: [PATCH 099/114] Update lint rules, force testify/assert for tests Use testify's assert package instead of the standard library's testing package. --- .golangci.yml | 9 +- active_tcp_test.go | 6 +- agent_handlers_test.go | 10 +- agent_test.go | 501 +++++++++-------------------- agent_udpmux_test.go | 2 +- candidate_relay_test.go | 2 +- candidate_server_reflexive_test.go | 2 +- candidate_test.go | 98 ++---- candidatepair_test.go | 8 +- connectivity_vnet_test.go | 48 ++- errors.go | 1 - gather_test.go | 13 +- gather_vnet_test.go | 156 +++------ icecontrol_test.go | 114 +++---- mdns_test.go | 8 +- net_test.go | 26 +- networktype_test.go | 16 +- priority_test.go | 29 +- rand_test.go | 9 +- selection_test.go | 2 +- tcp_mux_multi_test.go | 2 +- tcp_mux_test.go | 8 +- transport_test.go | 158 ++++----- transport_vnet_test.go | 2 +- udp_mux_multi_test.go | 2 +- udp_mux_test.go | 4 +- udp_mux_universal_test.go | 2 +- usecandidate_test.go | 19 +- 28 files changed, 388 insertions(+), 869 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 88cb4fb..120faf2 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -19,12 +19,16 @@ linters-settings: recommendations: - errors forbidigo: + analyze-types: true forbid: - ^fmt.Print(f|ln)?$ - ^log.(Panic|Fatal|Print)(f|ln)?$ - ^os.Exit$ - ^panic$ - ^print(ln)?$ + - p: ^testing.T.(Error|Errorf|Fatal|Fatalf|Fail|FailNow)$ + pkg: ^testing$ + msg: "use testify/assert instead" varnamelen: max-distance: 12 min-name-length: 2 @@ -127,9 +131,12 @@ issues: exclude-dirs-use-default: false exclude-rules: # Allow complex tests and examples, better to be self contained - - path: (examples|main\.go|_test\.go) + - path: (examples|main\.go) linters: + - gocognit - forbidigo + - path: _test\.go + linters: - gocognit # Allow forbidden identifiers in CLI commands diff --git a/active_tcp_test.go b/active_tcp_test.go index 796292b..b47ff6a 100644 --- a/active_tcp_test.go +++ b/active_tcp_test.go @@ -147,7 +147,7 @@ func TestActiveTCP(t *testing.T) { req.NoError(err) req.NotNil(activeAgent) - passiveAgentConn, activeAgenConn := connect(passiveAgent, activeAgent) + passiveAgentConn, activeAgenConn := connect(t, passiveAgent, activeAgent) req.NotNil(passiveAgentConn) req.NotNil(activeAgenConn) @@ -220,7 +220,7 @@ func TestActiveTCP_NonBlocking(t *testing.T) { require.NoError(t, aAgent.AddRemoteCandidate(invalidCandidate)) require.NoError(t, bAgent.AddRemoteCandidate(invalidCandidate)) - connect(aAgent, bAgent) + connect(t, aAgent, bAgent) <-isConnected } @@ -284,7 +284,7 @@ func TestActiveTCP_Respect_NetworkTypes(t *testing.T) { require.NoError(t, aAgent.AddRemoteCandidate(invalidCandidate)) require.NoError(t, bAgent.AddRemoteCandidate(invalidCandidate)) - connect(aAgent, bAgent) + connect(t, aAgent, bAgent) <-isConnected require.NoError(t, tcpListener.Close()) diff --git a/agent_handlers_test.go b/agent_handlers_test.go index 5518f26..0c980f6 100644 --- a/agent_handlers_test.go +++ b/agent_handlers_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/pion/transport/v3/test" + "github.com/stretchr/testify/assert" ) func TestConnectionStateNotifier(t *testing.T) { @@ -33,7 +34,7 @@ func TestConnectionStateNotifier(t *testing.T) { } select { case <-updates: - t.Errorf("received more updates than expected") + t.Errorf("received more updates than expected") // nolint case <-time.After(1 * time.Second): } close(done) @@ -53,14 +54,11 @@ func TestConnectionStateNotifier(t *testing.T) { done := make(chan struct{}) go func() { for i := 0; i < 10000; i++ { - x := <-updates - if x != ConnectionState(i) { - t.Errorf("expected %d got %d", x, i) - } + assert.Equal(t, ConnectionState(i), <-updates) } select { case <-updates: - t.Errorf("received more updates than expected") + t.Errorf("received more updates than expected") // nolint case <-time.After(1 * time.Second): } close(done) diff --git a/agent_test.go b/agent_test.go index 1ec75c1..0d97ea6 100644 --- a/agent_test.go +++ b/agent_test.go @@ -8,7 +8,6 @@ package ice import ( "context" - "errors" "net" "strconv" "sync" @@ -57,9 +56,7 @@ func TestHandlePeerReflexive(t *testing.T) { //nolint:cyclop } local, err := NewCandidateHost(&hostConfig) local.conn = &fakenet.MockPacketConn{} - if err != nil { - t.Fatalf("failed to create a new candidate: %v", err) - } + require.NoError(t, err) remote := &net.UDPAddr{IP: net.ParseIP("172.17.0.3"), Port: 999} @@ -77,29 +74,17 @@ func TestHandlePeerReflexive(t *testing.T) { //nolint:cyclop agent.handleInbound(msg, local, remote) // Length of remote candidate list must be one now - if len(agent.remoteCandidates) != 1 { - t.Fatal("failed to add a network type to the remote candidate list") - } + require.Len(t, agent.remoteCandidates, 1) // Length of remote candidate list for a network type must be 1 set := agent.remoteCandidates[local.NetworkType()] - if len(set) != 1 { - t.Fatal("failed to add prflx candidate to remote candidate list") - } + require.Len(t, set, 1) c := set[0] - if c.Type() != CandidateTypePeerReflexive { - t.Fatal("candidate type must be prflx") - } - - if c.Address() != "172.17.0.3" { - t.Fatal("IP address mismatch") - } - - if c.Port() != 999 { - t.Fatal("Port number mismatch") - } + require.Equal(t, CandidateTypePeerReflexive, c.Type()) + require.Equal(t, "172.17.0.3", c.Address()) + require.Equal(t, 999, c.Port()) })) }) @@ -120,18 +105,13 @@ func TestHandlePeerReflexive(t *testing.T) { //nolint:cyclop Component: 1, } local, err := NewCandidateHost(&hostConfig) - if err != nil { - t.Fatalf("failed to create a new candidate: %v", err) - } + require.NoError(t, err) remote := &BadAddr{} // nolint: contextcheck agent.handleInbound(nil, local, remote) - - if len(agent.remoteCandidates) != 0 { - t.Fatal("bad address should not be added to the remote candidate list") - } + require.Len(t, agent.remoteCandidates, 0) })) }) @@ -158,9 +138,7 @@ func TestHandlePeerReflexive(t *testing.T) { //nolint:cyclop } local, err := NewCandidateHost(&hostConfig) local.conn = &fakenet.MockPacketConn{} - if err != nil { - t.Fatalf("failed to create a new candidate: %v", err) - } + require.NoError(t, err) remote := &net.UDPAddr{IP: net.ParseIP("172.17.0.3"), Port: 999} msg, err := stun.Build(stun.BindingSuccess, stun.NewTransactionIDSetter(tID), @@ -171,9 +149,7 @@ func TestHandlePeerReflexive(t *testing.T) { //nolint:cyclop // nolint: contextcheck agent.handleInbound(msg, local, remote) - if len(agent.remoteCandidates) != 0 { - t.Fatal("unknown remote was able to create a candidate") - } + require.Len(t, agent.remoteCandidates, 0) })) }) } @@ -248,7 +224,7 @@ func TestConnectivityOnStartup(t *testing.T) { bUfrag, bPwd, err := bAgent.GetLocalUserCredentials() require.NoError(t, err) - gatherAndExchangeCandidates(aAgent, bAgent) + gatherAndExchangeCandidates(t, aAgent, bAgent) accepted := make(chan struct{}) accepting := make(chan struct{}) @@ -256,9 +232,9 @@ func TestConnectivityOnStartup(t *testing.T) { origHdlr := aAgent.onConnectionStateChangeHdlr.Load() if origHdlr != nil { - defer check(aAgent.OnConnectionStateChange(origHdlr.(func(ConnectionState)))) //nolint:forcetypeassert + defer require.NoError(t, aAgent.OnConnectionStateChange(origHdlr.(func(ConnectionState)))) //nolint:forcetypeassert } - check(aAgent.OnConnectionStateChange(func(s ConnectionState) { + require.NoError(t, aAgent.OnConnectionStateChange(func(s ConnectionState) { if s == ConnectionStateChecking { close(accepting) } @@ -270,14 +246,14 @@ func TestConnectivityOnStartup(t *testing.T) { go func() { var acceptErr error aConn, acceptErr = aAgent.Accept(context.TODO(), bUfrag, bPwd) - check(acceptErr) + require.NoError(t, acceptErr) close(accepted) }() <-accepting bConn, err := bAgent.Dial(context.TODO(), aUfrag, aPwd) - check(err) + require.NoError(t, err) // Ensure accepted <-accepted @@ -346,7 +322,7 @@ func TestConnectivityLite(t *testing.T) { }() require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) - connectWithVNet(aAgent, bAgent) + connectWithVNet(t, aAgent, bAgent) // Ensure pair selected // Note: this assumes ConnectionStateConnected is thrown after selecting the final pair @@ -377,65 +353,47 @@ func TestInboundValidity(t *testing.T) { //nolint:cyclop } local, err := NewCandidateHost(&hostConfig) local.conn = &fakenet.MockPacketConn{} - if err != nil { - t.Fatalf("failed to create a new candidate: %v", err) - } + require.NoError(t, err) t.Run("Invalid Binding requests should be discarded", func(t *testing.T) { agent, err := NewAgent(&AgentConfig{}) - if err != nil { - t.Fatalf("Error constructing ice.Agent") - } + require.NoError(t, err) defer func() { require.NoError(t, agent.Close()) }() agent.handleInbound(buildMsg(stun.ClassRequest, "invalid", agent.localPwd), local, remote) - if len(agent.remoteCandidates) == 1 { - t.Fatal("Binding with invalid Username was able to create prflx candidate") - } + require.Len(t, agent.remoteCandidates, 0) agent.handleInbound(buildMsg(stun.ClassRequest, agent.localUfrag+":"+agent.remoteUfrag, "Invalid"), local, remote) - if len(agent.remoteCandidates) == 1 { - t.Fatal("Binding with invalid MessageIntegrity was able to create prflx candidate") - } + require.Len(t, agent.remoteCandidates, 0) }) t.Run("Invalid Binding success responses should be discarded", func(t *testing.T) { a, err := NewAgent(&AgentConfig{}) - if err != nil { - t.Fatalf("Error constructing ice.Agent") - } + require.NoError(t, err) defer func() { require.NoError(t, a.Close()) }() a.handleInbound(buildMsg(stun.ClassSuccessResponse, a.localUfrag+":"+a.remoteUfrag, "Invalid"), local, remote) - if len(a.remoteCandidates) == 1 { - t.Fatal("Binding with invalid MessageIntegrity was able to create prflx candidate") - } + require.Len(t, a.remoteCandidates, 0) }) t.Run("Discard non-binding messages", func(t *testing.T) { a, err := NewAgent(&AgentConfig{}) - if err != nil { - t.Fatalf("Error constructing ice.Agent") - } + require.NoError(t, err) defer func() { require.NoError(t, a.Close()) }() a.handleInbound(buildMsg(stun.ClassErrorResponse, a.localUfrag+":"+a.remoteUfrag, "Invalid"), local, remote) - if len(a.remoteCandidates) == 1 { - t.Fatal("non-binding message was able to create prflxRemote") - } + require.Len(t, a.remoteCandidates, 0) }) t.Run("Valid bind request", func(t *testing.T) { a, err := NewAgent(&AgentConfig{}) - if err != nil { - t.Fatalf("Error constructing ice.Agent") - } + require.NoError(t, err) defer func() { require.NoError(t, a.Close()) }() @@ -444,9 +402,7 @@ func TestInboundValidity(t *testing.T) { //nolint:cyclop a.selector = &controllingSelector{agent: a, log: a.log} // nolint: contextcheck a.handleInbound(buildMsg(stun.ClassRequest, a.localUfrag+":"+a.remoteUfrag, a.localPwd), local, remote) - if len(a.remoteCandidates) != 1 { - t.Fatal("Binding with valid values was unable to create prflx candidate") - } + require.Len(t, a.remoteCandidates, 1) }) require.NoError(t, err) @@ -469,17 +425,13 @@ func TestInboundValidity(t *testing.T) { //nolint:cyclop // nolint: contextcheck agent.handleInbound(msg, local, remote) - if len(agent.remoteCandidates) != 1 { - t.Fatal("Binding with valid values (but no fingerprint) was unable to create prflx candidate") - } + require.Len(t, agent.remoteCandidates, 1) })) }) t.Run("Success with invalid TransactionID", func(t *testing.T) { agent, err := NewAgent(&AgentConfig{}) - if err != nil { - t.Fatalf("Error constructing ice.Agent") - } + require.NoError(t, err) defer func() { require.NoError(t, agent.Close()) }() @@ -492,9 +444,7 @@ func TestInboundValidity(t *testing.T) { //nolint:cyclop } local, err := NewCandidateHost(&hostConfig) local.conn = &fakenet.MockPacketConn{} - if err != nil { - t.Fatalf("failed to create a new candidate: %v", err) - } + require.NoError(t, err) remote := &net.UDPAddr{IP: net.ParseIP("172.17.0.3"), Port: 999} tID := [stun.TransactionIDSize]byte{} @@ -506,9 +456,7 @@ func TestInboundValidity(t *testing.T) { //nolint:cyclop require.NoError(t, err) agent.handleInbound(msg, local, remote) - if len(agent.remoteCandidates) != 0 { - t.Fatal("unknown remote was able to create a candidate") - } + require.Len(t, agent.remoteCandidates, 0) }) } @@ -525,21 +473,17 @@ func TestInvalidAgentStarts(t *testing.T) { ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) defer cancel() - if _, err = agent.Dial(ctx, "", "bar"); err != nil && !errors.Is(err, ErrRemoteUfragEmpty) { - t.Fatal(err) - } + _, err = agent.Dial(ctx, "", "bar") + require.ErrorIs(t, ErrRemoteUfragEmpty, err) - if _, err = agent.Dial(ctx, "foo", ""); err != nil && !errors.Is(err, ErrRemotePwdEmpty) { - t.Fatal(err) - } + _, err = agent.Dial(ctx, "foo", "") + require.ErrorIs(t, ErrRemotePwdEmpty, err) - if _, err = agent.Dial(ctx, "foo", "bar"); err != nil && !errors.Is(err, ErrCanceledByCaller) { - t.Fatal(err) - } + _, err = agent.Dial(ctx, "foo", "bar") + require.ErrorIs(t, ErrCanceledByCaller, err) - if _, err = agent.Dial(context.TODO(), "foo", "bar"); err != nil && !errors.Is(err, ErrMultipleStart) { - t.Fatal(err) - } + _, err = agent.Dial(ctx, "foo", "bar") + require.ErrorIs(t, ErrMultipleStart, err) } // Assert that Agent emits Connecting/Connected/Disconnected/Failed/Closed messages. @@ -606,7 +550,7 @@ func TestConnectionStateCallback(t *testing.T) { //nolint:cyclop }) require.NoError(t, err) - connect(aAgent, bAgent) + connect(t, aAgent, bAgent) <-isChecking <-isConnected @@ -622,17 +566,13 @@ func TestConnectionStateCallback(t *testing.T) { //nolint:cyclop func TestInvalidGather(t *testing.T) { t.Run("Gather with no OnCandidate should error", func(t *testing.T) { a, err := NewAgent(&AgentConfig{}) - if err != nil { - t.Fatalf("Error constructing ice.Agent") - } + require.NoError(t, err) defer func() { require.NoError(t, a.Close()) }() err = a.GatherCandidates() - if !errors.Is(err, ErrNoOnCandidateHandler) { - t.Fatal("trickle GatherCandidates succeeded without OnCandidate") - } + require.ErrorIs(t, ErrNoOnCandidateHandler, err) }) } @@ -643,9 +583,7 @@ func TestCandidatePairsStats(t *testing.T) { //nolint:cyclop,gocyclo defer test.TimeOut(1 * time.Second).Stop() agent, err := NewAgent(&AgentConfig{}) - if err != nil { - t.Fatalf("Failed to create agent: %s", err) - } + require.NoError(t, err) defer func() { require.NoError(t, agent.Close()) }() @@ -657,9 +595,7 @@ func TestCandidatePairsStats(t *testing.T) { //nolint:cyclop,gocyclo Component: 1, } hostLocal, err := NewCandidateHost(hostConfig) - if err != nil { - t.Fatalf("Failed to construct local host candidate: %s", err) - } + require.NoError(t, err) relayConfig := &CandidateRelayConfig{ Network: "udp", @@ -670,9 +606,7 @@ func TestCandidatePairsStats(t *testing.T) { //nolint:cyclop,gocyclo RelPort: 43210, } relayRemote, err := NewCandidateRelay(relayConfig) - if err != nil { - t.Fatalf("Failed to construct remote relay candidate: %s", err) - } + require.NoError(t, err) srflxConfig := &CandidateServerReflexiveConfig{ Network: "udp", @@ -683,9 +617,7 @@ func TestCandidatePairsStats(t *testing.T) { //nolint:cyclop,gocyclo RelPort: 43212, } srflxRemote, err := NewCandidateServerReflexive(srflxConfig) - if err != nil { - t.Fatalf("Failed to construct remote srflx candidate: %s", err) - } + require.NoError(t, err) prflxConfig := &CandidatePeerReflexiveConfig{ Network: "udp", @@ -696,9 +628,7 @@ func TestCandidatePairsStats(t *testing.T) { //nolint:cyclop,gocyclo RelPort: 43211, } prflxRemote, err := NewCandidatePeerReflexive(prflxConfig) - if err != nil { - t.Fatalf("Failed to construct remote prflx candidate: %s", err) - } + require.NoError(t, err) hostConfig = &CandidateHostConfig{ Network: "udp", @@ -707,9 +637,7 @@ func TestCandidatePairsStats(t *testing.T) { //nolint:cyclop,gocyclo Component: 1, } hostRemote, err := NewCandidateHost(hostConfig) - if err != nil { - t.Fatalf("Failed to construct remote host candidate: %s", err) - } + require.NoError(t, err) for _, remote := range []Candidate{relayRemote, srflxRemote, prflxRemote, hostRemote} { p := agent.findPair(hostLocal, remote) @@ -731,16 +659,12 @@ func TestCandidatePairsStats(t *testing.T) { //nolint:cyclop,gocyclo } stats := agent.GetCandidatePairsStats() - if len(stats) != 4 { - t.Fatal("expected 4 candidate pairs stats") - } + require.Len(t, stats, 4) var relayPairStat, srflxPairStat, prflxPairStat, hostPairStat CandidatePairStats for _, cps := range stats { - if cps.LocalCandidateID != hostLocal.ID() { - t.Fatal("invalid local candidate id") - } + require.Equal(t, cps.LocalCandidateID, hostLocal.ID()) switch cps.RemoteCandidateID { case relayRemote.ID(): relayPairStat = cps @@ -751,54 +675,30 @@ func TestCandidatePairsStats(t *testing.T) { //nolint:cyclop,gocyclo case hostRemote.ID(): hostPairStat = cps default: - t.Fatal("invalid remote candidate ID") + t.Fatal("invalid remote candidate ID") //nolint } - if cps.FirstRequestTimestamp.IsZero() || cps.LastRequestTimestamp.IsZero() || - cps.FirstResponseTimestamp.IsZero() || cps.LastResponseTimestamp.IsZero() || - cps.FirstRequestReceivedTimestamp.IsZero() || cps.LastRequestReceivedTimestamp.IsZero() || - cps.RequestsReceived == 0 || cps.RequestsSent == 0 || cps.ResponsesSent == 0 || cps.ResponsesReceived == 0 { - t.Fatal("failed to verify pair stats counter and timestamps", cps) - } + require.False(t, cps.FirstRequestTimestamp.IsZero()) + require.False(t, cps.LastRequestTimestamp.IsZero()) + require.False(t, cps.FirstResponseTimestamp.IsZero()) + require.False(t, cps.LastResponseTimestamp.IsZero()) + require.False(t, cps.FirstRequestReceivedTimestamp.IsZero()) + require.False(t, cps.LastRequestReceivedTimestamp.IsZero()) + require.NotZero(t, cps.RequestsReceived) + require.NotZero(t, cps.RequestsSent) + require.NotZero(t, cps.ResponsesSent) + require.NotZero(t, cps.ResponsesReceived) } - if relayPairStat.RemoteCandidateID != relayRemote.ID() { - t.Fatal("missing host-relay pair stat") - } + require.Equal(t, relayPairStat.RemoteCandidateID, relayRemote.ID()) + require.Equal(t, srflxPairStat.RemoteCandidateID, srflxRemote.ID()) + require.Equal(t, prflxPairStat.RemoteCandidateID, prflxRemote.ID()) + require.Equal(t, hostPairStat.RemoteCandidateID, hostRemote.ID()) + require.Equal(t, prflxPairStat.State, CandidatePairStateFailed) - if srflxPairStat.RemoteCandidateID != srflxRemote.ID() { - t.Fatal("missing host-srflx pair stat") - } - - if prflxPairStat.RemoteCandidateID != prflxRemote.ID() { - t.Fatal("missing host-prflx pair stat") - } - - if hostPairStat.RemoteCandidateID != hostRemote.ID() { - t.Fatal("missing host-host pair stat") - } - - if prflxPairStat.State != CandidatePairStateFailed { - t.Fatalf("expected host-prflx pair to have state failed, it has state %s instead", - prflxPairStat.State.String()) - } - - expectedCurrentRoundTripTime := time.Duration(10) * time.Second - if prflxPairStat.CurrentRoundTripTime != expectedCurrentRoundTripTime.Seconds() { - t.Fatalf("expected current round trip time to be %f, it is %f instead", - expectedCurrentRoundTripTime.Seconds(), prflxPairStat.CurrentRoundTripTime) - } - - expectedTotalRoundTripTime := time.Duration(55) * time.Second - if prflxPairStat.TotalRoundTripTime != expectedTotalRoundTripTime.Seconds() { - t.Fatalf("expected total round trip time to be %f, it is %f instead", - expectedTotalRoundTripTime.Seconds(), prflxPairStat.TotalRoundTripTime) - } - - if prflxPairStat.ResponsesReceived != 10 { - t.Fatalf("expected responses received to be 10, it is %d instead", - prflxPairStat.ResponsesReceived) - } + require.Equal(t, float64(10), prflxPairStat.CurrentRoundTripTime) + require.Equal(t, float64(55), prflxPairStat.TotalRoundTripTime) + require.Equal(t, uint64(10), prflxPairStat.ResponsesReceived) } func TestSelectedCandidatePairStats(t *testing.T) { //nolint:cyclop @@ -808,9 +708,7 @@ func TestSelectedCandidatePairStats(t *testing.T) { //nolint:cyclop defer test.TimeOut(1 * time.Second).Stop() agent, err := NewAgent(&AgentConfig{}) - if err != nil { - t.Fatalf("Failed to create agent: %s", err) - } + require.NoError(t, err) defer func() { require.NoError(t, agent.Close()) }() @@ -822,9 +720,7 @@ func TestSelectedCandidatePairStats(t *testing.T) { //nolint:cyclop Component: 1, } hostLocal, err := NewCandidateHost(hostConfig) - if err != nil { - t.Fatalf("Failed to construct local host candidate: %s", err) - } + require.NoError(t, err) srflxConfig := &CandidateServerReflexiveConfig{ Network: "udp", @@ -835,9 +731,7 @@ func TestSelectedCandidatePairStats(t *testing.T) { //nolint:cyclop RelPort: 43212, } srflxRemote, err := NewCandidateServerReflexive(srflxConfig) - if err != nil { - t.Fatalf("Failed to construct remote srflx candidate: %s", err) - } + require.NoError(t, err) // no selected pair, should return not available _, ok := agent.GetSelectedCandidatePairStats() @@ -859,29 +753,11 @@ func TestSelectedCandidatePairStats(t *testing.T) { //nolint:cyclop stats, ok := agent.GetSelectedCandidatePairStats() require.True(t, ok) - if stats.LocalCandidateID != hostLocal.ID() { - t.Fatal("invalid local candidate id") - } - if stats.RemoteCandidateID != srflxRemote.ID() { - t.Fatal("invalid remote candidate id") - } - - expectedCurrentRoundTripTime := time.Duration(10) * time.Second - if stats.CurrentRoundTripTime != expectedCurrentRoundTripTime.Seconds() { - t.Fatalf("expected current round trip time to be %f, it is %f instead", - expectedCurrentRoundTripTime.Seconds(), stats.CurrentRoundTripTime) - } - - expectedTotalRoundTripTime := time.Duration(55) * time.Second - if stats.TotalRoundTripTime != expectedTotalRoundTripTime.Seconds() { - t.Fatalf("expected total round trip time to be %f, it is %f instead", - expectedTotalRoundTripTime.Seconds(), stats.TotalRoundTripTime) - } - - if stats.ResponsesReceived != 10 { - t.Fatalf("expected responses received to be 10, it is %d instead", - stats.ResponsesReceived) - } + require.Equal(t, stats.LocalCandidateID, hostLocal.ID()) + require.Equal(t, stats.RemoteCandidateID, srflxRemote.ID()) + require.Equal(t, float64(10), stats.CurrentRoundTripTime) + require.Equal(t, float64(55), stats.TotalRoundTripTime) + require.Equal(t, uint64(10), stats.ResponsesReceived) } func TestLocalCandidateStats(t *testing.T) { //nolint:cyclop @@ -891,9 +767,7 @@ func TestLocalCandidateStats(t *testing.T) { //nolint:cyclop defer test.TimeOut(1 * time.Second).Stop() agent, err := NewAgent(&AgentConfig{}) - if err != nil { - t.Fatalf("Failed to create agent: %s", err) - } + require.NoError(t, err) defer func() { require.NoError(t, agent.Close()) }() @@ -905,9 +779,7 @@ func TestLocalCandidateStats(t *testing.T) { //nolint:cyclop Component: 1, } hostLocal, err := NewCandidateHost(hostConfig) - if err != nil { - t.Fatalf("Failed to construct local host candidate: %s", err) - } + require.NoError(t, err) srflxConfig := &CandidateServerReflexiveConfig{ Network: "udp", @@ -918,16 +790,12 @@ func TestLocalCandidateStats(t *testing.T) { //nolint:cyclop RelPort: 43212, } srflxLocal, err := NewCandidateServerReflexive(srflxConfig) - if err != nil { - t.Fatalf("Failed to construct local srflx candidate: %s", err) - } + require.NoError(t, err) agent.localCandidates[NetworkTypeUDP4] = []Candidate{hostLocal, srflxLocal} localStats := agent.GetLocalCandidatesStats() - if len(localStats) != 2 { - t.Fatalf("expected 2 local candidates stats, got %d instead", len(localStats)) - } + require.Len(t, localStats, 2) var hostLocalStat, srflxLocalStat CandidateStats for _, stats := range localStats { @@ -940,29 +808,16 @@ func TestLocalCandidateStats(t *testing.T) { //nolint:cyclop srflxLocalStat = stats candidate = srflxLocal default: - t.Fatal("invalid local candidate ID") + t.Fatal("invalid local candidate ID") // nolint } - if stats.CandidateType != candidate.Type() { - t.Fatal("invalid stats CandidateType") - } - - if stats.Priority != candidate.Priority() { - t.Fatal("invalid stats CandidateType") - } - - if stats.IP != candidate.Address() { - t.Fatal("invalid stats IP") - } + require.Equal(t, stats.CandidateType, candidate.Type()) + require.Equal(t, stats.Priority, candidate.Priority()) + require.Equal(t, stats.IP, candidate.Address()) } - if hostLocalStat.ID != hostLocal.ID() { - t.Fatal("missing host local stat") - } - - if srflxLocalStat.ID != srflxLocal.ID() { - t.Fatal("missing srflx local stat") - } + require.Equal(t, hostLocalStat.ID, hostLocal.ID()) + require.Equal(t, srflxLocalStat.ID, srflxLocal.ID()) } func TestRemoteCandidateStats(t *testing.T) { //nolint:cyclop @@ -972,9 +827,7 @@ func TestRemoteCandidateStats(t *testing.T) { //nolint:cyclop defer test.TimeOut(1 * time.Second).Stop() agent, err := NewAgent(&AgentConfig{}) - if err != nil { - t.Fatalf("Failed to create agent: %s", err) - } + require.NoError(t, err) defer func() { require.NoError(t, agent.Close()) }() @@ -988,9 +841,7 @@ func TestRemoteCandidateStats(t *testing.T) { //nolint:cyclop RelPort: 43210, } relayRemote, err := NewCandidateRelay(relayConfig) - if err != nil { - t.Fatalf("Failed to construct remote relay candidate: %s", err) - } + require.NoError(t, err) srflxConfig := &CandidateServerReflexiveConfig{ Network: "udp", @@ -1001,9 +852,7 @@ func TestRemoteCandidateStats(t *testing.T) { //nolint:cyclop RelPort: 43212, } srflxRemote, err := NewCandidateServerReflexive(srflxConfig) - if err != nil { - t.Fatalf("Failed to construct remote srflx candidate: %s", err) - } + require.NoError(t, err) prflxConfig := &CandidatePeerReflexiveConfig{ Network: "udp", @@ -1014,9 +863,7 @@ func TestRemoteCandidateStats(t *testing.T) { //nolint:cyclop RelPort: 43211, } prflxRemote, err := NewCandidatePeerReflexive(prflxConfig) - if err != nil { - t.Fatalf("Failed to construct remote prflx candidate: %s", err) - } + require.NoError(t, err) hostConfig := &CandidateHostConfig{ Network: "udp", @@ -1025,16 +872,12 @@ func TestRemoteCandidateStats(t *testing.T) { //nolint:cyclop Component: 1, } hostRemote, err := NewCandidateHost(hostConfig) - if err != nil { - t.Fatalf("Failed to construct remote host candidate: %s", err) - } + require.NoError(t, err) agent.remoteCandidates[NetworkTypeUDP4] = []Candidate{relayRemote, srflxRemote, prflxRemote, hostRemote} remoteStats := agent.GetRemoteCandidatesStats() - if len(remoteStats) != 4 { - t.Fatalf("expected 4 remote candidates stats, got %d instead", len(remoteStats)) - } + require.Len(t, remoteStats, 4) var relayRemoteStat, srflxRemoteStat, prflxRemoteStat, hostRemoteStat CandidateStats for _, stats := range remoteStats { var candidate Candidate @@ -1052,37 +895,18 @@ func TestRemoteCandidateStats(t *testing.T) { //nolint:cyclop hostRemoteStat = stats candidate = hostRemote default: - t.Fatal("invalid remote candidate ID") + t.Fatal("invalid remote candidate ID") // nolint } - if stats.CandidateType != candidate.Type() { - t.Fatal("invalid stats CandidateType") - } - - if stats.Priority != candidate.Priority() { - t.Fatal("invalid stats CandidateType") - } - - if stats.IP != candidate.Address() { - t.Fatal("invalid stats IP") - } + require.Equal(t, stats.CandidateType, candidate.Type()) + require.Equal(t, stats.Priority, candidate.Priority()) + require.Equal(t, stats.IP, candidate.Address()) } - if relayRemoteStat.ID != relayRemote.ID() { - t.Fatal("missing relay remote stat") - } - - if srflxRemoteStat.ID != srflxRemote.ID() { - t.Fatal("missing srflx remote stat") - } - - if prflxRemoteStat.ID != prflxRemote.ID() { - t.Fatal("missing prflx remote stat") - } - - if hostRemoteStat.ID != hostRemote.ID() { - t.Fatal("missing host remote stat") - } + require.Equal(t, relayRemoteStat.ID, relayRemote.ID()) + require.Equal(t, srflxRemoteStat.ID, srflxRemote.ID()) + require.Equal(t, prflxRemoteStat.ID, prflxRemote.ID()) + require.Equal(t, hostRemoteStat.ID, hostRemote.ID()) } func TestInitExtIPMapping(t *testing.T) { @@ -1090,13 +914,8 @@ func TestInitExtIPMapping(t *testing.T) { // agent.extIPMapper should be nil by default agent, err := NewAgent(&AgentConfig{}) - if err != nil { - t.Fatalf("Failed to create agent: %v", err) - } - if agent.extIPMapper != nil { - require.NoError(t, agent.Close()) - t.Fatal("a.extIPMapper should be nil by default") - } + require.NoError(t, err) + require.Nil(t, agent.extIPMapper) require.NoError(t, agent.Close()) // a.extIPMapper should be nil when NAT1To1IPs is a non-nil empty array @@ -1104,14 +923,8 @@ func TestInitExtIPMapping(t *testing.T) { NAT1To1IPs: []string{}, NAT1To1IPCandidateType: CandidateTypeHost, }) - if err != nil { - require.NoError(t, agent.Close()) - t.Fatalf("Failed to create agent: %v", err) - } - if agent.extIPMapper != nil { - require.NoError(t, agent.Close()) - t.Fatal("a.extIPMapper should be nil by default") - } + require.NoError(t, err) + require.Nil(t, agent.extIPMapper) require.NoError(t, agent.Close()) // NewAgent should return an error when 1:1 NAT for host candidate is enabled @@ -1121,9 +934,7 @@ func TestInitExtIPMapping(t *testing.T) { NAT1To1IPCandidateType: CandidateTypeHost, CandidateTypes: []CandidateType{CandidateTypeRelay}, }) - if !errors.Is(err, ErrIneffectiveNAT1To1IPMappingHost) { - t.Fatalf("Unexpected error: %v", err) - } + require.ErrorIs(t, ErrIneffectiveNAT1To1IPMappingHost, err) // NewAgent should return an error when 1:1 NAT for srflx candidate is enabled // but the candidate type does not appear in the CandidateTypes. @@ -1132,9 +943,7 @@ func TestInitExtIPMapping(t *testing.T) { NAT1To1IPCandidateType: CandidateTypeServerReflexive, CandidateTypes: []CandidateType{CandidateTypeRelay}, }) - if !errors.Is(err, ErrIneffectiveNAT1To1IPMappingSrflx) { - t.Fatalf("Unexpected error: %v", err) - } + require.ErrorIs(t, ErrIneffectiveNAT1To1IPMappingSrflx, err) // NewAgent should return an error when 1:1 NAT for host candidate is enabled // along with mDNS with MulticastDNSModeQueryAndGather @@ -1143,18 +952,14 @@ func TestInitExtIPMapping(t *testing.T) { NAT1To1IPCandidateType: CandidateTypeHost, MulticastDNSMode: MulticastDNSModeQueryAndGather, }) - if !errors.Is(err, ErrMulticastDNSWithNAT1To1IPMapping) { - t.Fatalf("Unexpected error: %v", err) - } + require.ErrorIs(t, ErrMulticastDNSWithNAT1To1IPMapping, err) // NewAgent should return if newExternalIPMapper() returns an error. _, err = NewAgent(&AgentConfig{ NAT1To1IPs: []string{"bad.2.3.4"}, // Bad IP NAT1To1IPCandidateType: CandidateTypeHost, }) - if !errors.Is(err, ErrInvalidNAT1To1IPMapping) { - t.Fatalf("Unexpected error: %v", err) - } + require.ErrorIs(t, ErrInvalidNAT1To1IPMapping, err) } func TestBindingRequestTimeout(t *testing.T) { @@ -1260,7 +1065,7 @@ func TestConnectionStateFailedDeleteAllCandidates(t *testing.T) { } })) - connect(aAgent, bAgent) + connect(t, aAgent, bAgent) <-isFailed done := make(chan struct{}) @@ -1312,7 +1117,7 @@ func TestConnectionStateConnectingToFailed(t *testing.T) { case ConnectionStateChecking: isChecking.Done() case ConnectionStateCompleted: - t.Errorf("Unexpected ConnectionState: %v", c) + t.Errorf("Unexpected ConnectionState: %v", c) //nolint default: } } @@ -1342,7 +1147,7 @@ func TestAgentRestart(t *testing.T) { oneSecond := time.Second t.Run("Restart During Gather", func(t *testing.T) { - connA, connB := pipe(&AgentConfig{ + connA, connB := pipe(t, &AgentConfig{ DisconnectedTimeout: &oneSecond, FailedTimeout: &oneSecond, }) @@ -1370,7 +1175,7 @@ func TestAgentRestart(t *testing.T) { }) t.Run("Restart One Side", func(t *testing.T) { - connA, connB := pipe(&AgentConfig{ + connA, connB := pipe(t, &AgentConfig{ DisconnectedTimeout: &oneSecond, FailedTimeout: &oneSecond, }) @@ -1401,7 +1206,7 @@ func TestAgentRestart(t *testing.T) { } // Store the original candidates, confirm that after we reconnect we have new pairs - connA, connB := pipe(&AgentConfig{ + connA, connB := pipe(t, &AgentConfig{ DisconnectedTimeout: &oneSecond, FailedTimeout: &oneSecond, }) @@ -1428,7 +1233,7 @@ func TestAgentRestart(t *testing.T) { require.NoError(t, err) require.NoError(t, connB.agent.SetRemoteCredentials(ufrag, pwd)) - gatherAndExchangeCandidates(connA.agent, connB.agent) + gatherAndExchangeCandidates(t, connA.agent, connB.agent) // Wait until both have gone back to connected <-aConnected @@ -1443,9 +1248,7 @@ func TestAgentRestart(t *testing.T) { func TestGetRemoteCredentials(t *testing.T) { var config AgentConfig agent, err := NewAgent(&config) - if err != nil { - t.Fatalf("Error constructing ice.Agent: %v", err) - } + require.NoError(t, err) defer func() { require.NoError(t, agent.Close()) }() @@ -1464,9 +1267,7 @@ func TestGetRemoteCandidates(t *testing.T) { var config AgentConfig agent, err := NewAgent(&config) - if err != nil { - t.Fatalf("Error constructing ice.Agent: %v", err) - } + require.NoError(t, err) defer func() { require.NoError(t, agent.Close()) }() @@ -1498,9 +1299,7 @@ func TestGetLocalCandidates(t *testing.T) { var config AgentConfig agent, err := NewAgent(&config) - if err != nil { - t.Fatalf("Error constructing ice.Agent: %v", err) - } + require.NoError(t, err) defer func() { require.NoError(t, agent.Close()) }() @@ -1580,7 +1379,7 @@ func TestCloseInConnectionStateCallback(t *testing.T) { }) require.NoError(t, err) - connect(aAgent, bAgent) + connect(t, aAgent, bAgent) close(isConnected) <-isClosed @@ -1605,12 +1404,12 @@ func TestRunTaskInConnectionStateCallback(t *testing.T) { } aAgent, err := NewAgent(cfg) - check(err) + require.NoError(t, err) defer func() { require.NoError(t, aAgent.Close()) }() bAgent, err := NewAgent(cfg) - check(err) + require.NoError(t, err) defer func() { require.NoError(t, bAgent.Close()) }() @@ -1626,7 +1425,7 @@ func TestRunTaskInConnectionStateCallback(t *testing.T) { }) require.NoError(t, err) - connect(aAgent, bAgent) + connect(t, aAgent, bAgent) <-isComplete } @@ -1650,36 +1449,35 @@ func TestRunTaskInSelectedCandidatePairChangeCallback(t *testing.T) { } aAgent, err := NewAgent(cfg) - check(err) + require.NoError(t, err) defer func() { require.NoError(t, aAgent.Close()) }() bAgent, err := NewAgent(cfg) - check(err) + require.NoError(t, err) defer func() { require.NoError(t, bAgent.Close()) }() isComplete := make(chan interface{}) isTested := make(chan interface{}) - if err = aAgent.OnSelectedCandidatePairChange(func(Candidate, Candidate) { + err = aAgent.OnSelectedCandidatePairChange(func(Candidate, Candidate) { go func() { _, _, errCred := aAgent.GetLocalUserCredentials() require.NoError(t, errCred) close(isTested) }() - }); err != nil { - t.Error(err) - } - if err = aAgent.OnConnectionStateChange(func(c ConnectionState) { + }) + require.NoError(t, err) + + err = aAgent.OnConnectionStateChange(func(c ConnectionState) { if c == ConnectionStateConnected { close(isComplete) } - }); err != nil { - t.Error(err) - } + }) + require.NoError(t, err) - connect(aAgent, bAgent) + connect(t, aAgent, bAgent) <-isComplete <-isTested @@ -1746,7 +1544,7 @@ func TestLiteLifecycle(t *testing.T) { } })) - connectWithVNet(bAgent, aAgent) + connectWithVNet(t, bAgent, aAgent) <-aConnected <-bConnected @@ -1821,7 +1619,7 @@ func TestGetSelectedCandidatePair(t *testing.T) { require.NoError(t, err) require.Nil(t, bAgentPair) - connect(aAgent, bAgent) + connect(t, aAgent, bAgent) aAgentPair, err = aAgent.GetSelectedCandidatePair() require.NoError(t, err) @@ -1920,7 +1718,7 @@ func TestAcceptAggressiveNomination(t *testing.T) { //nolint:cyclop }() require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) - connect(aAgent, bAgent) + connect(t, aAgent, bAgent) // Ensure pair selected // Note: this assumes ConnectionStateConnected is thrown after selecting the final pair @@ -2008,11 +1806,8 @@ func TestAcceptAggressiveNomination(t *testing.T) { //nolint:cyclop case selected := <-selectedCh: require.True(t, selected.Equal(expectNewSelectedCandidate)) default: - if !tc.isExpectedToSwitch { - require.True(t, aAgent.getSelectedPair().Remote.Equal(expectNewSelectedCandidate)) - } else { - t.Fatal("No selected candidate pair") - } + require.False(t, tc.isExpectedToSwitch) + require.True(t, aAgent.getSelectedPair().Remote.Equal(expectNewSelectedCandidate)) } }) } @@ -2053,15 +1848,13 @@ func TestAgentGracefulCloseDeadlock(t *testing.T) { closeNow.Add(1) closed.Add(2) closeHdlr := func(agent *Agent, agentClosed *bool) { - check(agent.OnConnectionStateChange(func(cs ConnectionState) { + require.NoError(t, agent.OnConnectionStateChange(func(cs ConnectionState) { if cs == ConnectionStateConnected { connected.Done() closeNow.Wait() go func() { - if err := agent.GracefulClose(); err != nil { - require.NoError(t, err) - } + require.NoError(t, agent.GracefulClose()) *agentClosed = true closed.Done() }() @@ -2073,7 +1866,7 @@ func TestAgentGracefulCloseDeadlock(t *testing.T) { closeHdlr(bAgent, &bAgentClosed) t.Log("connecting agents") - _, _ = connect(aAgent, bAgent) + _, _ = connect(t, aAgent, bAgent) t.Log("waiting for them to confirm connection in callback") connected.Wait() @@ -2087,9 +1880,7 @@ func TestSetCandidatesUfrag(t *testing.T) { var config AgentConfig agent, err := NewAgent(&config) - if err != nil { - t.Fatalf("Error constructing ice.Agent: %v", err) - } + require.NoError(t, err) defer func() { require.NoError(t, agent.Close()) }() @@ -2129,9 +1920,7 @@ func TestAlwaysSentKeepAlive(t *testing.T) { //nolint:cyclop defer test.TimeOut(1 * time.Second).Stop() agent, err := NewAgent(&AgentConfig{}) - if err != nil { - t.Fatalf("Failed to create agent: %s", err) - } + require.NoError(t, err) defer func() { require.NoError(t, agent.Close()) }() @@ -2139,11 +1928,9 @@ func TestAlwaysSentKeepAlive(t *testing.T) { //nolint:cyclop log := logging.NewDefaultLoggerFactory().NewLogger("agent") agent.selector = &controllingSelector{agent: agent, log: log} pair := makeCandidatePair(t) - if s, ok := pair.Local.(*CandidateHost); ok { - s.conn = &fakenet.MockPacketConn{} - } else { - t.Fatalf("Invalid local candidate") - } + s, ok := pair.Local.(*CandidateHost) + require.True(t, ok) + s.conn = &fakenet.MockPacketConn{} agent.setSelectedPair(pair) pair.Remote.seen(false) diff --git a/agent_udpmux_test.go b/agent_udpmux_test.go index 1dd70c7..ec71bee 100644 --- a/agent_udpmux_test.go +++ b/agent_udpmux_test.go @@ -71,7 +71,7 @@ func TestMuxAgent(t *testing.T) { require.NoError(t, agent.Close()) }() - conn, muxedConn := connect(agent, muxedA) + conn, muxedConn := connect(t, agent, muxedA) pair := muxedA.getSelectedPair() require.NotNil(t, pair) diff --git a/candidate_relay_test.go b/candidate_relay_test.go index f22af07..1a04506 100644 --- a/candidate_relay_test.go +++ b/candidate_relay_test.go @@ -80,7 +80,7 @@ func TestRelayOnlyConnection(t *testing.T) { bNotifier, bConnected := onConnected() require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) - connect(aAgent, bAgent) + connect(t, aAgent, bAgent) <-aConnected <-bConnected } diff --git a/candidate_server_reflexive_test.go b/candidate_server_reflexive_test.go index 77d344a..c3e91aa 100644 --- a/candidate_server_reflexive_test.go +++ b/candidate_server_reflexive_test.go @@ -73,7 +73,7 @@ func TestServerReflexiveOnlyConnection(t *testing.T) { bNotifier, bConnected := onConnected() require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) - connect(aAgent, bAgent) + connect(t, aAgent, bAgent) <-aConnected <-bConnected } diff --git a/candidate_test.go b/candidate_test.go index ac7b642..a89632f 100644 --- a/candidate_test.go +++ b/candidate_test.go @@ -176,9 +176,7 @@ func TestCandidatePriority(t *testing.T) { WantPriority: 16777215, }, } { - if got, want := test.Candidate.Priority(), test.WantPriority; got != want { - t.Fatalf("Candidate(%v).Priority() = %d, want %d", test.Candidate, got, want) - } + require.Equal(t, test.Candidate.Priority(), test.WantPriority) } } @@ -271,9 +269,7 @@ func mustCandidateHost(t *testing.T, conf *CandidateHostConfig) Candidate { t.Helper() cand, err := NewCandidateHost(conf) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) return cand } @@ -286,9 +282,7 @@ func mustCandidateHostWithExtensions( t.Helper() cand, err := NewCandidateHost(conf) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) cand.setExtensions(extensions) @@ -299,9 +293,7 @@ func mustCandidateRelay(t *testing.T, conf *CandidateRelayConfig) Candidate { t.Helper() cand, err := NewCandidateRelay(conf) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) return cand } @@ -314,9 +306,7 @@ func mustCandidateRelayWithExtensions( t.Helper() cand, err := NewCandidateRelay(conf) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) cand.setExtensions(extensions) @@ -327,9 +317,7 @@ func mustCandidateServerReflexive(t *testing.T, conf *CandidateServerReflexiveCo t.Helper() cand, err := NewCandidateServerReflexive(conf) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) return cand } @@ -342,9 +330,7 @@ func mustCandidateServerReflexiveWithExtensions( t.Helper() cand, err := NewCandidateServerReflexive(conf) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) cand.setExtensions(extensions) @@ -359,9 +345,7 @@ func mustCandidatePeerReflexiveWithExtensions( t.Helper() cand, err := NewCandidatePeerReflexive(conf) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) cand.setExtensions(extensions) @@ -603,7 +587,7 @@ func TestCandidateWriteTo(t *testing.T) { }) require.NoError(t, err, "error creating test TCP listener") - conn, err := net.DialTCP("tcp", nil, listener.Addr().(*net.TCPAddr)) + conn, err := net.DialTCP("tcp", nil, listener.Addr().(*net.TCPAddr)) // nolint require.NoError(t, err, "error dialing test TCP connection") loggerFactory := logging.NewDefaultLoggerFactory() @@ -1049,9 +1033,7 @@ func TestCandidateGetExtension(t *testing.T) { Priority: 500, Foundation: "750", }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) candidate.setExtensions(extensions) @@ -1086,9 +1068,7 @@ func TestCandidateGetExtension(t *testing.T) { Priority: 500, Foundation: "750", }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) candidate.setExtensions(extensions) @@ -1111,9 +1091,7 @@ func TestCandidateGetExtension(t *testing.T) { Foundation: "750", TCPType: TCPTypeActive, }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) tcpType, ok := candidate.GetExtension("tcptype") @@ -1136,9 +1114,7 @@ func TestCandidateGetExtension(t *testing.T) { Priority: 500, Foundation: "750", }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) tcpType, ok = candidate2.GetExtension("tcptype") @@ -1163,9 +1139,7 @@ func TestBaseCandidateMarshalExtensions(t *testing.T) { Priority: 500, Foundation: "750", }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) candidate.setExtensions(extensions) @@ -1181,9 +1155,7 @@ func TestBaseCandidateMarshalExtensions(t *testing.T) { Priority: 500, Foundation: "750", }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) value := candidate.marshalExtensions() require.Equal(t, "", value) @@ -1198,9 +1170,7 @@ func TestBaseCandidateMarshalExtensions(t *testing.T) { Foundation: "750", TCPType: TCPTypeActive, }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) value := candidate.marshalExtensions() require.Equal(t, "tcptype active", value) @@ -1296,9 +1266,7 @@ func TestBaseCandidateExtensionsEqual(t *testing.T) { Priority: 500, Foundation: "750", }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) cand.setExtensions(testCase.extensions1) @@ -1316,9 +1284,7 @@ func TestCandidateAddExtension(t *testing.T) { Priority: 500, Foundation: "750", }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) require.NoError(t, candidate.AddExtension(CandidateExtension{"a", "b"})) require.NoError(t, candidate.AddExtension(CandidateExtension{"c", "d"})) @@ -1335,9 +1301,7 @@ func TestCandidateAddExtension(t *testing.T) { Priority: 500, Foundation: "750", }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) require.NoError(t, candidate.AddExtension(CandidateExtension{"a", "b"})) require.NoError(t, candidate.AddExtension(CandidateExtension{"a", "d"})) @@ -1355,9 +1319,7 @@ func TestCandidateAddExtension(t *testing.T) { Foundation: "750", TCPType: TCPTypeActive, }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) ext, ok := candidate.GetExtension("tcptype") require.True(t, ok) @@ -1380,9 +1342,7 @@ func TestCandidateAddExtension(t *testing.T) { Priority: 500, Foundation: "750", }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) require.NoError(t, candidate.AddExtension(CandidateExtension{"tcptype", "active"})) @@ -1401,9 +1361,7 @@ func TestCandidateAddExtension(t *testing.T) { Priority: 500, Foundation: "750", }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) require.Error(t, candidate.AddExtension(CandidateExtension{"", ""})) @@ -1424,9 +1382,7 @@ func TestCandidateRemoveExtension(t *testing.T) { Priority: 500, Foundation: "750", }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) require.NoError(t, candidate.AddExtension(CandidateExtension{"a", "b"})) require.NoError(t, candidate.AddExtension(CandidateExtension{"c", "d"})) @@ -1445,9 +1401,7 @@ func TestCandidateRemoveExtension(t *testing.T) { Priority: 500, Foundation: "750", }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) require.NoError(t, candidate.AddExtension(CandidateExtension{"a", "b"})) require.NoError(t, candidate.AddExtension(CandidateExtension{"c", "d"})) @@ -1467,9 +1421,7 @@ func TestCandidateRemoveExtension(t *testing.T) { Foundation: "750", TCPType: TCPTypeActive, }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // tcptype extension should be removed, even if it's not in the extensions list (Not Parsed) require.True(t, candidate.RemoveExtension("tcptype")) diff --git a/candidatepair_test.go b/candidatepair_test.go index a338a69..f6e8284 100644 --- a/candidatepair_test.go +++ b/candidatepair_test.go @@ -115,9 +115,7 @@ func TestCandidatePairPriority(t *testing.T) { WantPriority: 72057593987596287, }, } { - if got, want := test.Pair.priority(), test.WantPriority; got != want { - t.Fatalf("CandidatePair(%v).Priority() = %d, want %d", test.Pair, got, want) - } + require.Equal(t, test.Pair.priority(), test.WantPriority) } } @@ -125,9 +123,7 @@ func TestCandidatePairEquality(t *testing.T) { pairA := newCandidatePair(hostCandidate(), srflxCandidate(), true) pairB := newCandidatePair(hostCandidate(), srflxCandidate(), false) - if !pairA.equal(pairB) { - t.Fatalf("Expected %v to equal %v", pairA, pairB) - } + require.True(t, pairA.equal(pairB)) } func TestNilCandidatePairString(t *testing.T) { diff --git a/connectivity_vnet_test.go b/connectivity_vnet_test.go index 5ab5b56..2064c46 100644 --- a/connectivity_vnet_test.go +++ b/connectivity_vnet_test.go @@ -200,15 +200,16 @@ func addVNetSTUN(wanNet *vnet.Net, loggerFactory logging.LoggerFactory) (*turn.S return server, err } -func connectWithVNet(aAgent, bAgent *Agent) (*Conn, *Conn) { +func connectWithVNet(t *testing.T, aAgent, bAgent *Agent) (*Conn, *Conn) { + t.Helper() // Manual signaling aUfrag, aPwd, err := aAgent.GetLocalUserCredentials() - check(err) + require.NoError(t, err) bUfrag, bPwd, err := bAgent.GetLocalUserCredentials() - check(err) + require.NoError(t, err) - gatherAndExchangeCandidates(aAgent, bAgent) + gatherAndExchangeCandidates(t, aAgent, bAgent) accepted := make(chan struct{}) var aConn *Conn @@ -216,12 +217,12 @@ func connectWithVNet(aAgent, bAgent *Agent) (*Conn, *Conn) { go func() { var acceptErr error aConn, acceptErr = aAgent.Accept(context.TODO(), bUfrag, bPwd) - check(acceptErr) + require.NoError(t, acceptErr) close(accepted) }() bConn, err := bAgent.Dial(context.TODO(), aUfrag, aPwd) - check(err) + require.NoError(t, err) // Ensure accepted <-accepted @@ -234,7 +235,8 @@ type agentTestConfig struct { nat1To1IPCandidateType CandidateType } -func pipeWithVNet(vnet *virtualNet, a0TestConfig, a1TestConfig *agentTestConfig) (*Conn, *Conn) { +func pipeWithVNet(t *testing.T, vnet *virtualNet, a0TestConfig, a1TestConfig *agentTestConfig) (*Conn, *Conn) { + t.Helper() aNotifier, aConnected := onConnected() bNotifier, bConnected := onConnected() @@ -255,13 +257,8 @@ func pipeWithVNet(vnet *virtualNet, a0TestConfig, a1TestConfig *agentTestConfig) } aAgent, err := NewAgent(cfg0) - if err != nil { - panic(err) - } - err = aAgent.OnConnectionStateChange(aNotifier) - if err != nil { - panic(err) - } + require.NoError(t, err) + require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) if a1TestConfig.nat1To1IPCandidateType != CandidateTypeUnspecified { nat1To1IPs = []string{ @@ -278,15 +275,10 @@ func pipeWithVNet(vnet *virtualNet, a0TestConfig, a1TestConfig *agentTestConfig) } bAgent, err := NewAgent(cfg1) - if err != nil { - panic(err) - } - err = bAgent.OnConnectionStateChange(bNotifier) - if err != nil { - panic(err) - } + require.NoError(t, err) + require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) - aConn, bConn := connectWithVNet(aAgent, bAgent) + aConn, bConn := connectWithVNet(t, aAgent, bAgent) // Ensure pair selected // Note: this assumes ConnectionStateConnected is thrown after selecting the final pair @@ -347,7 +339,7 @@ func TestConnectivityVNet(t *testing.T) { stunServerURL, }, } - ca, cb := pipeWithVNet(vnet, a0TestConfig, a1TestConfig) + ca, cb := pipeWithVNet(t, vnet, a0TestConfig, a1TestConfig) time.Sleep(1 * time.Second) @@ -381,7 +373,7 @@ func TestConnectivityVNet(t *testing.T) { stunServerURL, }, } - ca, cb := pipeWithVNet(vnet, a0TestConfig, a1TestConfig) + ca, cb := pipeWithVNet(t, vnet, a0TestConfig, a1TestConfig) log.Debug("Closing...") closePipe(t, ca, cb) @@ -413,7 +405,7 @@ func TestConnectivityVNet(t *testing.T) { a1TestConfig := &agentTestConfig{ urls: []*stun.URI{}, } - ca, cb := pipeWithVNet(vnet, a0TestConfig, a1TestConfig) + ca, cb := pipeWithVNet(t, vnet, a0TestConfig, a1TestConfig) log.Debug("Closing...") closePipe(t, ca, cb) @@ -445,7 +437,7 @@ func TestConnectivityVNet(t *testing.T) { a1TestConfig := &agentTestConfig{ urls: []*stun.URI{}, } - ca, cb := pipeWithVNet(vnet, a0TestConfig, a1TestConfig) + ca, cb := pipeWithVNet(t, vnet, a0TestConfig, a1TestConfig) log.Debug("Closing...") closePipe(t, ca, cb) @@ -527,7 +519,7 @@ func TestDisconnectedToConnected(t *testing.T) { controlledStateChanges <- c })) - connectWithVNet(controllingAgent, controlledAgent) + connectWithVNet(t, controllingAgent, controlledAgent) blockUntilStateSeen := func(expectedState ConnectionState, stateQueue chan ConnectionState) { for s := range stateQueue { if s == expectedState { @@ -618,7 +610,7 @@ func TestWriteUseValidPair(t *testing.T) { require.NoError(t, controlledAgent.Close()) }() - gatherAndExchangeCandidates(controllingAgent, controlledAgent) + gatherAndExchangeCandidates(t, controllingAgent, controlledAgent) controllingUfrag, controllingPwd, err := controllingAgent.GetLocalUserCredentials() require.NoError(t, err) diff --git a/errors.go b/errors.go index 39b22e5..a803665 100644 --- a/errors.go +++ b/errors.go @@ -136,7 +136,6 @@ var ( errParseRelatedAddr = errors.New("failed to parse related addresses") errParseExtension = errors.New("failed to parse extension") errParseTCPType = errors.New("failed to parse TCP type") - errRead = errors.New("failed to read") errUDPMuxDisabled = errors.New("UDPMux is not enabled") errUnknownRole = errors.New("unknown role") errWrite = errors.New("failed to write") diff --git a/gather_test.go b/gather_test.go index d450187..6ef39e1 100644 --- a/gather_test.go +++ b/gather_test.go @@ -12,7 +12,6 @@ import ( "io" "net" "net/url" - "reflect" "sort" "strconv" "sync" @@ -78,19 +77,13 @@ func TestListenUDP(t *testing.T) { require.NoError(t, err) p, _ := strconv.Atoi(port) - if p < portMin || p > portMax { - t.Fatalf("listenUDP with port restriction [%d, %d] listened on incorrect port (%s)", portMin, portMax, port) - } + require.False(t, p < portMin || p > portMax) result = append(result, p) portRange = append(portRange, portMin+i) } - if sort.IntsAreSorted(result) { - t.Fatalf("listenUDP with port restriction [%d, %d], ports result should be random", portMin, portMax) - } + require.False(t, sort.IntsAreSorted(result)) sort.Ints(result) - if !reflect.DeepEqual(result, portRange) { - t.Fatalf("listenUDP with port restriction [%d, %d], got:%v, want:%v", portMin, portMax, result, portRange) - } + require.Equal(t, result, portRange) _, err = listenUDPInPortRange(agent.net, agent.log, portMax, portMin, udp, &net.UDPAddr{IP: ip, Port: 0}) require.Equal(t, err, ErrPort, "listenUDP with port restriction [%d, %d], did not return ErrPort", portMin, portMax) } diff --git a/gather_vnet_test.go b/gather_vnet_test.go index c5a3f1d..3020367 100644 --- a/gather_vnet_test.go +++ b/gather_vnet_test.go @@ -8,7 +8,6 @@ package ice import ( "context" - "errors" "fmt" "net" "testing" @@ -38,36 +37,25 @@ func TestVNetGather(t *testing.T) { //nolint:cyclop }() _, localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) - if len(localIPs) > 0 { - t.Fatal("should return no local IP") - } + require.Len(t, localIPs, 0) require.NoError(t, err) }) t.Run("Gather a dynamic IP address", func(t *testing.T) { cider := "1.2.3.0/24" _, ipNet, err := net.ParseCIDR(cider) - if err != nil { - t.Fatalf("Failed to parse CIDR: %s", err) - } + require.NoError(t, err) router, err := vnet.NewRouter(&vnet.RouterConfig{ CIDR: cider, LoggerFactory: loggerFactory, }) - if err != nil { - t.Fatalf("Failed to create a router: %s", err) - } + require.NoError(t, err) nw, err := vnet.NewNet(&vnet.NetConfig{}) - if err != nil { - t.Fatalf("Failed to create a Net: %s", err) - } + require.NoError(t, err) - err = router.AddNet(nw) - if err != nil { - t.Fatalf("Failed to add a Net to the router: %s", err) - } + require.NoError(t, router.AddNet(nw)) a, err := NewAgent(&AgentConfig{ Net: nw, @@ -78,18 +66,12 @@ func TestVNetGather(t *testing.T) { //nolint:cyclop }() _, localAddrs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{NetworkTypeUDP4}, false) - if len(localAddrs) == 0 { - t.Fatal("should have one local IP") - } + require.Len(t, localAddrs, 1) require.NoError(t, err) for _, addr := range localAddrs { - if addr.IsLoopback() { - t.Fatal("should not return loopback IP") - } - if !ipNet.Contains(addr.AsSlice()) { - t.Fatal("should be contained in the CIDR") - } + require.False(t, addr.IsLoopback()) + require.True(t, ipNet.Contains(addr.AsSlice())) } }) @@ -98,24 +80,15 @@ func TestVNetGather(t *testing.T) { //nolint:cyclop CIDR: "1.2.3.0/24", LoggerFactory: loggerFactory, }) - if err != nil { - t.Fatalf("Failed to create a router: %s", err) - } + require.NoError(t, err) nw, err := vnet.NewNet(&vnet.NetConfig{}) - if err != nil { - t.Fatalf("Failed to create a Net: %s", err) - } + require.NoError(t, err) - err = router.AddNet(nw) - if err != nil { - t.Fatalf("Failed to add a Net to the router: %s", err) - } + require.NoError(t, router.AddNet(nw)) agent, err := NewAgent(&AgentConfig{Net: nw}) - if err != nil { - t.Fatalf("Failed to create agent: %s", err) - } + require.NoError(t, err) defer func() { require.NoError(t, agent.Close()) }() @@ -127,35 +100,22 @@ func TestVNetGather(t *testing.T) { //nolint:cyclop []NetworkType{NetworkTypeUDP4}, false, ) - if len(localAddrs) == 0 { - t.Fatal("localInterfaces found no interfaces, unable to test") - } + require.NotEqual(t, 0, len(localAddrs)) require.NoError(t, err) ip := localAddrs[0].AsSlice() conn, err := listenUDPInPortRange(agent.net, agent.log, 0, 0, udp, &net.UDPAddr{IP: ip, Port: 0}) - if err != nil { - t.Fatalf("listenUDP error with no port restriction %v", err) - } else if conn == nil { - t.Fatalf("listenUDP error with no port restriction return a nil conn") - } - err = conn.Close() - if err != nil { - t.Fatalf("failed to close conn") - } + require.NoError(t, err) + require.NotNil(t, conn) + require.NoError(t, conn.Close()) _, err = listenUDPInPortRange(agent.net, agent.log, 4999, 5000, udp, &net.UDPAddr{IP: ip, Port: 0}) - if !errors.Is(err, ErrPort) { - t.Fatal("listenUDP with invalid port range did not return ErrPort") - } + require.ErrorIs(t, ErrPort, err) conn, err = listenUDPInPortRange(agent.net, agent.log, 5000, 5000, udp, &net.UDPAddr{IP: ip, Port: 0}) - if err != nil { - t.Fatalf("listenUDP error with no port restriction %v", err) - } else if conn == nil { - t.Fatalf("listenUDP error with no port restriction return a nil conn") - } + require.NoError(t, err) + require.NotNil(t, conn) defer func() { require.NoError(t, conn.Close()) }() @@ -163,9 +123,7 @@ func TestVNetGather(t *testing.T) { //nolint:cyclop _, port, err := net.SplitHostPort(conn.LocalAddr().String()) require.NoError(t, err) - if port != "5000" { - t.Fatalf("listenUDP with port restriction of 5000 listened on incorrect port (%s)", port) - } + require.Equal(t, "5000", port) }) } @@ -205,9 +163,7 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { //nolint:cyclop nw, err := vnet.NewNet(&vnet.NetConfig{ StaticIPs: []string{localIP0, localIP1}, }) - if err != nil { - t.Fatalf("Failed to create a Net: %s", err) - } + require.NoError(t, err) err = lan.AddNet(nw) require.NoError(t, err, "should succeed") @@ -242,38 +198,22 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { //nolint:cyclop candidates, err := agent.GetLocalCandidates() require.NoError(t, err, "should succeed") - if len(candidates) != 2 { - t.Fatal("There must be two candidates") - } + require.Len(t, candidates, 2) lAddr := [2]*net.UDPAddr{nil, nil} for i, candi := range candidates { lAddr[i] = candi.(*CandidateHost).conn.LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert - if candi.Port() != lAddr[i].Port { - t.Fatalf("Unexpected candidate port: %d", candi.Port()) - } + require.Equal(t, candi.Port(), lAddr[i].Port) } if candidates[0].Address() == externalIP0 { //nolint:nestif - if candidates[1].Address() != externalIP1 { - t.Fatalf("Unexpected candidate IP: %s", candidates[1].Address()) - } - if lAddr[0].IP.String() != localIP0 { - t.Fatalf("Unexpected listen IP: %s", lAddr[0].IP.String()) - } - if lAddr[1].IP.String() != localIP1 { - t.Fatalf("Unexpected listen IP: %s", lAddr[1].IP.String()) - } + require.Equal(t, candidates[1].Address(), externalIP1) + require.Equal(t, lAddr[0].IP.String(), localIP0) + require.Equal(t, lAddr[1].IP.String(), localIP1) } else if candidates[0].Address() == externalIP1 { - if candidates[1].Address() != externalIP0 { - t.Fatalf("Unexpected candidate IP: %s", candidates[1].Address()) - } - if lAddr[0].IP.String() != localIP1 { - t.Fatalf("Unexpected listen IP: %s", lAddr[0].IP.String()) - } - if lAddr[1].IP.String() != localIP0 { - t.Fatalf("Unexpected listen IP: %s", lAddr[1].IP.String()) - } + require.Equal(t, candidates[1].Address(), externalIP0) + require.Equal(t, lAddr[0].IP.String(), localIP1) + require.Equal(t, lAddr[1].IP.String(), localIP0) } }) @@ -304,9 +244,7 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { //nolint:cyclop "10.0.0.1", }, }) - if err != nil { - t.Fatalf("Failed to create a Net: %s", err) - } + require.NoError(t, err) err = lan.AddNet(nw) require.NoError(t, err, "should succeed") @@ -344,9 +282,7 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { //nolint:cyclop candidates, err := agent.GetLocalCandidates() require.NoError(t, err, "should succeed") - if len(candidates) != 2 { - t.Fatalf("Expected two candidates. actually %d", len(candidates)) - } + require.Len(t, candidates, 2) var candiHost *CandidateHost var candiSrflx *CandidateServerReflexive @@ -358,7 +294,7 @@ func TestVNetGatherWithNAT1To1(t *testing.T) { //nolint:cyclop case *CandidateServerReflexive: candiSrflx = candi default: - t.Fatal("Unexpected candidate type") + t.Fatal("Unexpected candidate type") // nolint } } @@ -377,18 +313,11 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { CIDR: "1.2.3.0/24", LoggerFactory: loggerFactory, }) - if err != nil { - t.Fatalf("Failed to create a router: %s", err) - } + require.NoError(t, err) nw, err := vnet.NewNet(&vnet.NetConfig{}) - if err != nil { - t.Fatalf("Failed to create a Net: %s", err) - } - - if err = router.AddNet(nw); err != nil { - t.Fatalf("Failed to add a Net to the router: %s", err) - } + require.NoError(t, err) + require.NoError(t, router.AddNet(nw)) t.Run("InterfaceFilter should exclude the interface", func(t *testing.T) { agent, err := NewAgent(&AgentConfig{ @@ -412,10 +341,7 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { false, ) require.NoError(t, err) - - if len(localIPs) != 0 { - t.Fatal("InterfaceFilter should have excluded everything") - } + require.Len(t, localIPs, 0) }) t.Run("IPFilter should exclude the IP", func(t *testing.T) { @@ -440,10 +366,7 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { false, ) require.NoError(t, err) - - if len(localIPs) != 0 { - t.Fatal("IPFilter should have excluded everything") - } + require.Len(t, localIPs, 0) }) t.Run("InterfaceFilter should not exclude the interface", func(t *testing.T) { @@ -468,10 +391,7 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) { false, ) require.NoError(t, err) - - if len(localIPs) == 0 { - t.Fatal("InterfaceFilter should not have excluded anything") - } + require.Len(t, localIPs, 1) }) } diff --git a/icecontrol_test.go b/icecontrol_test.go index 8d710d9..1ea25a4 100644 --- a/icecontrol_test.go +++ b/icecontrol_test.go @@ -4,69 +4,52 @@ package ice import ( - "errors" "testing" "github.com/pion/stun/v3" + "github.com/stretchr/testify/require" ) func TestControlled_GetFrom(t *testing.T) { //nolint:dupl m := new(stun.Message) var attrCtr AttrControlled - if err := attrCtr.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) { - t.Error("unexpected error") - } - if err := m.Build(stun.BindingRequest, &attrCtr); err != nil { - t.Error(err) - } + require.ErrorIs(t, stun.ErrAttributeNotFound, attrCtr.GetFrom(m)) + require.NoError(t, m.Build(stun.BindingRequest, &attrCtr)) + m1 := new(stun.Message) - if _, err := m1.Write(m.Raw); err != nil { - t.Error(err) - } + _, err := m1.Write(m.Raw) + require.NoError(t, err) + var c1 AttrControlled - if err := c1.GetFrom(m1); err != nil { - t.Error(err) - } - if c1 != attrCtr { - t.Error("not equal") - } + require.NoError(t, c1.GetFrom(m1)) + require.Equal(t, c1, attrCtr) + t.Run("IncorrectSize", func(t *testing.T) { m3 := new(stun.Message) m3.Add(stun.AttrICEControlled, make([]byte, 100)) var c2 AttrControlled - if err := c2.GetFrom(m3); !stun.IsAttrSizeInvalid(err) { - t.Error("should error") - } + require.True(t, stun.IsAttrSizeInvalid(c2.GetFrom(m3))) }) } func TestControlling_GetFrom(t *testing.T) { //nolint:dupl m := new(stun.Message) var attrCtr AttrControlling - if err := attrCtr.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) { - t.Error("unexpected error") - } - if err := m.Build(stun.BindingRequest, &attrCtr); err != nil { - t.Error(err) - } + require.ErrorIs(t, stun.ErrAttributeNotFound, attrCtr.GetFrom(m)) + require.NoError(t, m.Build(stun.BindingRequest, &attrCtr)) + m1 := new(stun.Message) - if _, err := m1.Write(m.Raw); err != nil { - t.Error(err) - } + _, err := m1.Write(m.Raw) + require.NoError(t, err) + var c1 AttrControlling - if err := c1.GetFrom(m1); err != nil { - t.Error(err) - } - if c1 != attrCtr { - t.Error("not equal") - } + require.NoError(t, c1.GetFrom(m1)) + require.Equal(t, c1, attrCtr) t.Run("IncorrectSize", func(t *testing.T) { m3 := new(stun.Message) m3.Add(stun.AttrICEControlling, make([]byte, 100)) var c2 AttrControlling - if err := c2.GetFrom(m3); !stun.IsAttrSizeInvalid(err) { - t.Error("should error") - } + require.True(t, stun.IsAttrSizeInvalid(c2.GetFrom(m3))) }) } @@ -74,70 +57,49 @@ func TestControl_GetFrom(t *testing.T) { //nolint:cyclop t.Run("Blank", func(t *testing.T) { m := new(stun.Message) var c AttrControl - if err := c.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) { - t.Error("unexpected error") - } + require.ErrorIs(t, stun.ErrAttributeNotFound, c.GetFrom(m)) }) t.Run("Controlling", func(t *testing.T) { //nolint:dupl m := new(stun.Message) var attCtr AttrControl - if err := attCtr.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) { - t.Error("unexpected error") - } + require.ErrorIs(t, stun.ErrAttributeNotFound, attCtr.GetFrom(m)) attCtr.Role = Controlling attCtr.Tiebreaker = 4321 - if err := m.Build(stun.BindingRequest, &attCtr); err != nil { - t.Error(err) - } + require.NoError(t, m.Build(stun.BindingRequest, &attCtr)) m1 := new(stun.Message) - if _, err := m1.Write(m.Raw); err != nil { - t.Error(err) - } + _, err := m1.Write(m.Raw) + require.NoError(t, err) var c1 AttrControl - if err := c1.GetFrom(m1); err != nil { - t.Error(err) - } - if c1 != attCtr { - t.Error("not equal") - } + require.NoError(t, c1.GetFrom(m1)) + require.Equal(t, c1, attCtr) t.Run("IncorrectSize", func(t *testing.T) { m3 := new(stun.Message) m3.Add(stun.AttrICEControlling, make([]byte, 100)) var c2 AttrControl - if err := c2.GetFrom(m3); !stun.IsAttrSizeInvalid(err) { - t.Error("should error") - } + err := c2.GetFrom(m3) + require.True(t, stun.IsAttrSizeInvalid(err)) }) }) t.Run("Controlled", func(t *testing.T) { //nolint:dupl m := new(stun.Message) var attrCtrl AttrControl - if err := attrCtrl.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) { - t.Error("unexpected error") - } + require.ErrorIs(t, stun.ErrAttributeNotFound, attrCtrl.GetFrom(m)) attrCtrl.Role = Controlled attrCtrl.Tiebreaker = 1234 - if err := m.Build(stun.BindingRequest, &attrCtrl); err != nil { - t.Error(err) - } + require.NoError(t, m.Build(stun.BindingRequest, &attrCtrl)) m1 := new(stun.Message) - if _, err := m1.Write(m.Raw); err != nil { - t.Error(err) - } + _, err := m1.Write(m.Raw) + require.NoError(t, err) + var c1 AttrControl - if err := c1.GetFrom(m1); err != nil { - t.Error(err) - } - if c1 != attrCtrl { - t.Error("not equal") - } + require.NoError(t, c1.GetFrom(m1)) + require.Equal(t, c1, attrCtrl) t.Run("IncorrectSize", func(t *testing.T) { m3 := new(stun.Message) m3.Add(stun.AttrICEControlling, make([]byte, 100)) var c2 AttrControl - if err := c2.GetFrom(m3); !stun.IsAttrSizeInvalid(err) { - t.Error("should error") - } + err := c2.GetFrom(m3) + require.True(t, stun.IsAttrSizeInvalid(err)) }) }) } diff --git a/mdns_test.go b/mdns_test.go index a65d836..3dea4e4 100644 --- a/mdns_test.go +++ b/mdns_test.go @@ -65,7 +65,7 @@ func TestMulticastDNSOnlyConnection(t *testing.T) { bNotifier, bConnected := onConnected() require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) - connect(aAgent, bAgent) + connect(t, aAgent, bAgent) <-aConnected <-bConnected }) @@ -124,7 +124,7 @@ func TestMulticastDNSMixedConnection(t *testing.T) { bNotifier, bConnected := onConnected() require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) - connect(aAgent, bAgent) + connect(t, aAgent, bAgent) <-aConnected <-bConnected }) @@ -195,7 +195,5 @@ func TestGenerateMulticastDNSName(t *testing.T) { `^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}.local+$`, ).MatchString - if !isMDNSName(name) { - t.Fatalf("mDNS name must be UUID v4 + \".local\" suffix, got %s", name) - } + require.True(t, isMDNSName(name)) } diff --git a/net_test.go b/net_test.go index 8b7692e..8ebdf1f 100644 --- a/net_test.go +++ b/net_test.go @@ -13,25 +13,11 @@ import ( ) func TestIsSupportedIPv6Partial(t *testing.T) { - if isSupportedIPv6Partial(net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1}) { - t.Errorf("isSupportedIPv6Partial returned true with IPv4-compatible IPv6 address") - } - - if isSupportedIPv6Partial(net.ParseIP("fec0::2333")) { - t.Errorf("isSupportedIPv6Partial returned true with IPv6 site-local unicast address") - } - - if !isSupportedIPv6Partial(net.ParseIP("fe80::2333")) { - t.Errorf("isSupportedIPv6Partial returned false with IPv6 link-local address") - } - - if !isSupportedIPv6Partial(net.ParseIP("ff02::2333")) { - t.Errorf("isSupportedIPv6Partial returned false with IPv6 link-local multicast address") - } - - if !isSupportedIPv6Partial(net.ParseIP("2001::1")) { - t.Errorf("isSupportedIPv6Partial returned false with IPv6 global unicast address") - } + require.False(t, isSupportedIPv6Partial(net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1})) + require.False(t, isSupportedIPv6Partial(net.ParseIP("fec0::2333"))) + require.True(t, isSupportedIPv6Partial(net.ParseIP("fe80::2333"))) + require.True(t, isSupportedIPv6Partial(net.ParseIP("ff02::2333"))) + require.True(t, isSupportedIPv6Partial(net.ParseIP("2001::1"))) } func TestCreateAddr(t *testing.T) { @@ -67,7 +53,7 @@ func mustAddr(t *testing.T, ip net.IP) netip.Addr { t.Helper() addr, ok := netip.AddrFromSlice(ip) if !ok { - t.Fatal(ipConvertError{ip}) + t.Fatal(ipConvertError{ip}) // nolint } return addr diff --git a/networktype_test.go b/networktype_test.go index d327af6..31aba30 100644 --- a/networktype_test.go +++ b/networktype_test.go @@ -46,13 +46,8 @@ func TestNetworkTypeParsing_Success(t *testing.T) { }, } { actual, err := determineNetworkType(test.inNetwork, mustAddr(t, test.inIP)) - if err != nil { - t.Errorf("NetworkTypeParsing failed: %v", err) - } - if actual != test.expected { - t.Errorf("NetworkTypeParsing: '%s' -- input:%s expected:%s actual:%s", - test.name, test.inNetwork, test.expected, actual) - } + require.NoError(t, err) + require.Equal(t, test.expected, actual) } } @@ -70,11 +65,8 @@ func TestNetworkTypeParsing_Failure(t *testing.T) { ipv6, }, } { - actual, err := determineNetworkType(test.inNetwork, mustAddr(t, test.inIP)) - if err == nil { - t.Errorf("NetworkTypeParsing should fail: '%s' -- input:%s actual:%s", - test.name, test.inNetwork, actual) - } + _, err := determineNetworkType(test.inNetwork, mustAddr(t, test.inIP)) + require.Error(t, err) } } diff --git a/priority_test.go b/priority_test.go index b6b2c5d..eee418d 100644 --- a/priority_test.go +++ b/priority_test.go @@ -4,38 +4,29 @@ package ice import ( - "errors" "testing" "github.com/pion/stun/v3" + "github.com/stretchr/testify/require" ) func TestPriority_GetFrom(t *testing.T) { //nolint:dupl m := new(stun.Message) var priority PriorityAttr - if err := priority.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) { - t.Error("unexpected error") - } - if err := m.Build(stun.BindingRequest, &priority); err != nil { - t.Error(err) - } + require.ErrorIs(t, stun.ErrAttributeNotFound, priority.GetFrom(m)) + require.NoError(t, m.Build(stun.BindingRequest, &priority)) + m1 := new(stun.Message) - if _, err := m1.Write(m.Raw); err != nil { - t.Error(err) - } + _, err := m1.Write(m.Raw) + require.NoError(t, err) + var p1 PriorityAttr - if err := p1.GetFrom(m1); err != nil { - t.Error(err) - } - if p1 != priority { - t.Error("not equal") - } + require.NoError(t, p1.GetFrom(m1)) + require.Equal(t, p1, priority) t.Run("IncorrectSize", func(t *testing.T) { m3 := new(stun.Message) m3.Add(stun.AttrPriority, make([]byte, 100)) var p2 PriorityAttr - if err := p2.GetFrom(m3); !stun.IsAttrSizeInvalid(err) { - t.Error("should error") - } + require.True(t, stun.IsAttrSizeInvalid(p2.GetFrom(m3))) }) } diff --git a/rand_test.go b/rand_test.go index 9dd1187..420858c 100644 --- a/rand_test.go +++ b/rand_test.go @@ -67,15 +67,10 @@ func TestRandomGeneratorCollision(t *testing.T) { } wg.Wait() - if len(rands) != num { - t.Fatal("Failed to generate randoms") - } - + require.Len(t, rands, num) for i := 0; i < num; i++ { for j := i + 1; j < num; j++ { - if rands[i] == rands[j] { - t.Fatalf("generateRandString caused collision: %s == %s", rands[i], rands[j]) - } + require.NotEqual(t, rands[i], rands[j]) } } } diff --git a/selection_test.go b/selection_test.go index 7f4e5f3..810df8e 100644 --- a/selection_test.go +++ b/selection_test.go @@ -97,7 +97,7 @@ func TestBindingRequestHandler(t *testing.T) { require.NoError(t, err) require.NoError(t, controlledAgent.OnConnectionStateChange(bNotifier)) - controlledConn, controllingConn := connect(controlledAgent, controllingAgent) + controlledConn, controllingConn := connect(t, controlledAgent, controllingAgent) <-aConnected <-bConnected diff --git a/tcp_mux_multi_test.go b/tcp_mux_multi_test.go index 619b857..a73252c 100644 --- a/tcp_mux_multi_test.go +++ b/tcp_mux_multi_test.go @@ -61,7 +61,7 @@ func TestMultiTCPMux_Recv(t *testing.T) { defer func() { _ = pktConn.Close() }() - conn, err := net.DialTCP("tcp", nil, pktConn.LocalAddr().(*net.TCPAddr)) + conn, err := net.DialTCP("tcp", nil, pktConn.LocalAddr().(*net.TCPAddr)) // nolint require.NoError(t, err, "error dialing test TCP connection") msg := stun.New() diff --git a/tcp_mux_test.go b/tcp_mux_test.go index 53fa9ee..b33e399 100644 --- a/tcp_mux_test.go +++ b/tcp_mux_test.go @@ -51,7 +51,7 @@ func TestTCPMux_Recv(t *testing.T) { require.NotNil(t, tcpMux.LocalAddr(), "tcpMux.LocalAddr() is nil") - conn, err := net.DialTCP("tcp", nil, tcpMux.LocalAddr().(*net.TCPAddr)) + conn, err := net.DialTCP("tcp", nil, tcpMux.LocalAddr().(*net.TCPAddr)) // nolint require.NoError(t, err, "error dialing test TCP connection") msg := stun.New() @@ -150,7 +150,7 @@ func TestTCPMux_FirstPacketTimeout(t *testing.T) { require.NotNil(t, tcpMux.LocalAddr(), "tcpMux.LocalAddr() is nil") - conn, err := net.DialTCP("tcp", nil, tcpMux.LocalAddr().(*net.TCPAddr)) + conn, err := net.DialTCP("tcp", nil, tcpMux.LocalAddr().(*net.TCPAddr)) // nolint require.NoError(t, err, "error dialing test TCP connection") defer func() { _ = conn.Close() @@ -192,7 +192,7 @@ func TestTCPMux_NoLeakForConnectionFromStun(t *testing.T) { require.NotNil(t, tcpMux.LocalAddr(), "tcpMux.LocalAddr() is nil") t.Run("close connection from stun msg after timeout", func(t *testing.T) { - conn, err := net.DialTCP("tcp", nil, tcpMux.LocalAddr().(*net.TCPAddr)) + conn, err := net.DialTCP("tcp", nil, tcpMux.LocalAddr().(*net.TCPAddr)) // nolint require.NoError(t, err, "error dialing test TCP connection") defer func() { _ = conn.Close() @@ -217,7 +217,7 @@ func TestTCPMux_NoLeakForConnectionFromStun(t *testing.T) { }) t.Run("connection keep alive if access by user", func(t *testing.T) { - conn, err := net.DialTCP("tcp", nil, tcpMux.LocalAddr().(*net.TCPAddr)) + conn, err := net.DialTCP("tcp", nil, tcpMux.LocalAddr().(*net.TCPAddr)) // nolint require.NoError(t, err, "error dialing test TCP connection") defer func() { _ = conn.Close() diff --git a/transport_test.go b/transport_test.go index 0581c93..0377f81 100644 --- a/transport_test.go +++ b/transport_test.go @@ -38,10 +38,7 @@ func testTimeout(t *testing.T, conn *Conn, timeout time.Duration) { ticker := time.NewTicker(pollRate) defer func() { ticker.Stop() - err := conn.Close() - if err != nil { - t.Error(err) - } + require.NoError(t, conn.Close()) }() startedAt := time.Now() @@ -51,26 +48,18 @@ func testTimeout(t *testing.T, conn *Conn, timeout time.Duration) { var cs ConnectionState - err := conn.agent.loop.Run(context.Background(), func(_ context.Context) { + require.NoError(t, conn.agent.loop.Run(context.Background(), func(_ context.Context) { cs = conn.agent.connectionState - }) - if err != nil { - // We should never get here. - panic(err) - } + })) if cs != ConnectionStateConnected { elapsed := time.Since(startedAt) - if elapsed+margin < timeout { - t.Fatalf("Connection timed out %f msec early", elapsed.Seconds()*1000) - } else { - t.Logf("Connection timed out in %f msec", elapsed.Seconds()*1000) + require.Less(t, timeout, elapsed+margin) - return - } + return } } - t.Fatalf("Connection failed to time out in time. (expected timeout: %v)", timeout) + t.Fatalf("Connection failed to time out in time. (expected timeout: %v)", timeout) //nolint } func TestTimeout(t *testing.T) { @@ -85,24 +74,14 @@ func TestTimeout(t *testing.T) { defer test.TimeOut(time.Second * 20).Stop() t.Run("WithoutDisconnectTimeout", func(t *testing.T) { - ca, cb := pipe(nil) - err := cb.Close() - if err != nil { - // We should never get here. - panic(err) - } - + ca, cb := pipe(t, nil) + require.NoError(t, cb.Close()) testTimeout(t, ca, defaultDisconnectedTimeout) }) t.Run("WithDisconnectTimeout", func(t *testing.T) { - ca, cb := pipeWithTimeout(5*time.Second, 3*time.Second) - err := cb.Close() - if err != nil { - // We should never get here. - panic(err) - } - + ca, cb := pipeWithTimeout(t, 5*time.Second, 3*time.Second) + require.NoError(t, cb.Close()) testTimeout(t, ca, 5*time.Second) }) } @@ -114,31 +93,19 @@ func TestReadClosed(t *testing.T) { // Limit runtime in case of deadlocks defer test.TimeOut(time.Second * 20).Stop() - ca, cb := pipe(nil) - - err := ca.Close() - if err != nil { - // We should never get here. - panic(err) - } - - err = cb.Close() - if err != nil { - // We should never get here. - panic(err) - } + ca, cb := pipe(t, nil) + require.NoError(t, ca.Close()) + require.NoError(t, cb.Close()) empty := make([]byte, 10) - _, err = ca.Read(empty) - if err == nil { - t.Fatalf("Reading from a closed channel should return an error") - } + _, err := ca.Read(empty) + require.Error(t, err) } func stressDuplex(t *testing.T) { t.Helper() - ca, cb := pipe(nil) + ca, cb := pipe(t, nil) defer func() { require.NoError(t, ca.Close()) @@ -153,58 +120,52 @@ func stressDuplex(t *testing.T) { require.NoError(t, test.StressDuplex(ca, cb, opt)) } -func check(err error) { - if err != nil { - panic(err) - } -} - -func gatherAndExchangeCandidates(aAgent, bAgent *Agent) { +func gatherAndExchangeCandidates(t *testing.T, aAgent, bAgent *Agent) { + t.Helper() var wg sync.WaitGroup wg.Add(2) - check(aAgent.OnCandidate(func(candidate Candidate) { + require.NoError(t, aAgent.OnCandidate(func(candidate Candidate) { if candidate == nil { wg.Done() } })) - check(aAgent.GatherCandidates()) + require.NoError(t, aAgent.GatherCandidates()) - check(bAgent.OnCandidate(func(candidate Candidate) { + require.NoError(t, bAgent.OnCandidate(func(candidate Candidate) { if candidate == nil { wg.Done() } })) - check(bAgent.GatherCandidates()) + require.NoError(t, bAgent.GatherCandidates()) wg.Wait() candidates, err := aAgent.GetLocalCandidates() - check(err) + require.NoError(t, err) for _, c := range candidates { if addr, parseErr := netip.ParseAddr(c.Address()); parseErr == nil { - if shouldFilterLocationTrackedIP(addr) { - panic(addr) - } + require.False(t, shouldFilterLocationTrackedIP(addr)) } candidateCopy, copyErr := c.copy() - check(copyErr) - check(bAgent.AddRemoteCandidate(candidateCopy)) + require.NoError(t, copyErr) + require.NoError(t, bAgent.AddRemoteCandidate(candidateCopy)) } candidates, err = bAgent.GetLocalCandidates() - check(err) + require.NoError(t, err) for _, c := range candidates { candidateCopy, copyErr := c.copy() - check(copyErr) - check(aAgent.AddRemoteCandidate(candidateCopy)) + require.NoError(t, copyErr) + require.NoError(t, aAgent.AddRemoteCandidate(candidateCopy)) } } -func connect(aAgent, bAgent *Agent) (*Conn, *Conn) { - gatherAndExchangeCandidates(aAgent, bAgent) +func connect(t *testing.T, aAgent, bAgent *Agent) (*Conn, *Conn) { + t.Helper() + gatherAndExchangeCandidates(t, aAgent, bAgent) accepted := make(chan struct{}) var aConn *Conn @@ -212,15 +173,15 @@ func connect(aAgent, bAgent *Agent) (*Conn, *Conn) { go func() { var acceptErr error bUfrag, bPwd, acceptErr := bAgent.GetLocalUserCredentials() - check(acceptErr) + require.NoError(t, acceptErr) aConn, acceptErr = aAgent.Accept(context.TODO(), bUfrag, bPwd) - check(acceptErr) + require.NoError(t, acceptErr) close(accepted) }() aUfrag, aPwd, err := aAgent.GetLocalUserCredentials() - check(err) + require.NoError(t, err) bConn, err := bAgent.Dial(context.TODO(), aUfrag, aPwd) - check(err) + require.NoError(t, err) // Ensure accepted <-accepted @@ -228,7 +189,8 @@ func connect(aAgent, bAgent *Agent) (*Conn, *Conn) { return aConn, bConn } -func pipe(defaultConfig *AgentConfig) (*Conn, *Conn) { +func pipe(t *testing.T, defaultConfig *AgentConfig) (*Conn, *Conn) { + t.Helper() var urls []*stun.URI aNotifier, aConnected := onConnected() @@ -243,15 +205,15 @@ func pipe(defaultConfig *AgentConfig) (*Conn, *Conn) { cfg.NetworkTypes = supportedNetworkTypes() aAgent, err := NewAgent(cfg) - check(err) - check(aAgent.OnConnectionStateChange(aNotifier)) + require.NoError(t, err) + require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) bAgent, err := NewAgent(cfg) - check(err) + require.NoError(t, err) - check(bAgent.OnConnectionStateChange(bNotifier)) + require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) - aConn, bConn := connect(aAgent, bAgent) + aConn, bConn := connect(t, aAgent, bAgent) // Ensure pair selected // Note: this assumes ConnectionStateConnected is thrown after selecting the final pair @@ -261,7 +223,8 @@ func pipe(defaultConfig *AgentConfig) (*Conn, *Conn) { return aConn, bConn } -func pipeWithTimeout(disconnectTimeout time.Duration, iceKeepalive time.Duration) (*Conn, *Conn) { +func pipeWithTimeout(t *testing.T, disconnectTimeout time.Duration, iceKeepalive time.Duration) (*Conn, *Conn) { + t.Helper() var urls []*stun.URI aNotifier, aConnected := onConnected() @@ -275,14 +238,14 @@ func pipeWithTimeout(disconnectTimeout time.Duration, iceKeepalive time.Duration } aAgent, err := NewAgent(cfg) - check(err) - check(aAgent.OnConnectionStateChange(aNotifier)) + require.NoError(t, err) + require.NoError(t, aAgent.OnConnectionStateChange(aNotifier)) bAgent, err := NewAgent(cfg) - check(err) - check(bAgent.OnConnectionStateChange(bNotifier)) + require.NoError(t, err) + require.NoError(t, bAgent.OnConnectionStateChange(bNotifier)) - aConn, bConn := connect(aAgent, bAgent) + aConn, bConn := connect(t, aAgent, bAgent) // Ensure pair selected // Note: this assumes ConnectionStateConnected is thrown after selecting the final pair @@ -328,29 +291,22 @@ func TestConnStats(t *testing.T) { // Limit runtime in case of deadlocks defer test.TimeOut(time.Second * 20).Stop() - ca, cb := pipe(nil) - if _, err := ca.Write(make([]byte, 10)); err != nil { - t.Fatal("unexpected error trying to write") - } + ca, cb := pipe(t, nil) + _, err := ca.Write(make([]byte, 10)) + require.NoError(t, err) defer closePipe(t, ca, cb) var wg sync.WaitGroup wg.Add(1) go func() { buf := make([]byte, 10) - if _, err := cb.Read(buf); err != nil { - panic(errRead) - } + _, err := cb.Read(buf) + require.NoError(t, err) wg.Done() }() wg.Wait() - if ca.BytesSent() != 10 { - t.Fatal("bytes sent don't match") - } - - if cb.BytesReceived() != 10 { - t.Fatal("bytes received don't match") - } + require.Equal(t, uint64(10), ca.BytesSent()) + require.Equal(t, uint64(10), cb.BytesReceived()) } diff --git a/transport_vnet_test.go b/transport_vnet_test.go index 8eb2b6d..07e5940 100644 --- a/transport_vnet_test.go +++ b/transport_vnet_test.go @@ -53,7 +53,7 @@ func TestRemoteLocalAddr(t *testing.T) { }) t.Run("Remote/Local Pair Match between Agents", func(t *testing.T) { - ca, cb := pipeWithVNet(builtVnet, + ca, cb := pipeWithVNet(t, builtVnet, &agentTestConfig{ urls: []*stun.URI{stunServerURL}, }, diff --git a/udp_mux_multi_test.go b/udp_mux_multi_test.go index 0986fc1..4fc64de 100644 --- a/udp_mux_multi_test.go +++ b/udp_mux_multi_test.go @@ -103,7 +103,7 @@ func testMultiUDPMuxConnections(t *testing.T, udpMuxMulti *MultiUDPMuxDefault, u // Try talking with each PacketConn for _, pktConn := range pktConns { - remoteConn, err := net.DialUDP(network, nil, pktConn.LocalAddr().(*net.UDPAddr)) + remoteConn, err := net.DialUDP(network, nil, pktConn.LocalAddr().(*net.UDPAddr)) // nolint require.NoError(t, err, "error dialing test UDP connection") testMuxConnectionPair(t, pktConn, remoteConn, ufrag) } diff --git a/udp_mux_test.go b/udp_mux_test.go index 3a293ff..0aa7344 100644 --- a/udp_mux_test.go +++ b/udp_mux_test.go @@ -252,7 +252,7 @@ func verifyPacket(t *testing.T, b []byte, nextSeq uint32) { func TestUDPMux_Agent_Restart(t *testing.T) { oneSecond := time.Second - connA, connB := pipe(&AgentConfig{ + connA, connB := pipe(t, &AgentConfig{ DisconnectedTimeout: &oneSecond, FailedTimeout: &oneSecond, }) @@ -279,7 +279,7 @@ func TestUDPMux_Agent_Restart(t *testing.T) { require.NoError(t, connA.agent.SetRemoteCredentials(ufragB, pwdB)) require.NoError(t, connB.agent.SetRemoteCredentials(ufragA, pwdA)) - gatherAndExchangeCandidates(connA.agent, connB.agent) + gatherAndExchangeCandidates(t, connA.agent, connB.agent) // Wait until both have gone back to connected <-aConnected diff --git a/udp_mux_universal_test.go b/udp_mux_universal_test.go index 46f927a..1f648e6 100644 --- a/udp_mux_universal_test.go +++ b/udp_mux_universal_test.go @@ -51,7 +51,7 @@ func testMuxSrflxConnection(t *testing.T, udpMux *UniversalUDPMuxDefault, ufrag _ = pktConn.Close() }() - remoteConn, err := net.DialUDP(network, nil, &net.UDPAddr{ + remoteConn, err := net.DialUDP(network, nil, &net.UDPAddr{ // nolint Port: udpMux.LocalAddr().(*net.UDPAddr).Port, }) require.NoError(t, err, "error dialing test UDP connection") diff --git a/usecandidate_test.go b/usecandidate_test.go index c44409c..d50315a 100644 --- a/usecandidate_test.go +++ b/usecandidate_test.go @@ -7,21 +7,16 @@ import ( "testing" "github.com/pion/stun/v3" + "github.com/stretchr/testify/require" ) func TestUseCandidateAttr_AddTo(t *testing.T) { m := new(stun.Message) - if UseCandidate().IsSet(m) { - t.Error("should not be set") - } - if err := m.Build(stun.BindingRequest, UseCandidate()); err != nil { - t.Error(err) - } + require.False(t, UseCandidate().IsSet(m)) + require.NoError(t, m.Build(stun.BindingRequest, UseCandidate())) + m1 := new(stun.Message) - if _, err := m1.Write(m.Raw); err != nil { - t.Error(err) - } - if !UseCandidate().IsSet(m1) { - t.Error("should be set") - } + _, err := m1.Write(m.Raw) + require.NoError(t, err) + require.True(t, UseCandidate().IsSet(m1)) } From 753c2a0fe868006a9e8d341feb11fc6a5d54a45d Mon Sep 17 00:00:00 2001 From: Pion <59523206+pionbot@users.noreply.github.com> Date: Wed, 23 Apr 2025 14:41:01 +0000 Subject: [PATCH 100/114] Update CI configs to v0.11.19 Update lint scripts and CI configs. --- .github/workflows/release.yml | 2 +- .github/workflows/test.yaml | 6 +++--- .github/workflows/tidy-check.yaml | 2 +- .reuse/dep5 | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0e72ea4..b4967b2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,4 +21,4 @@ jobs: release: uses: pion/.goassets/.github/workflows/release.reusable.yml@master with: - go-version: "1.22" # auto-update/latest-go-version + go-version: "1.24" # auto-update/latest-go-version diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b024289..7713e93 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -23,7 +23,7 @@ jobs: uses: pion/.goassets/.github/workflows/test.reusable.yml@master strategy: matrix: - go: ["1.23", "1.22"] # auto-update/supported-go-version-list + go: ["1.24", "1.23"] # auto-update/supported-go-version-list fail-fast: false with: go-version: ${{ matrix.go }} @@ -33,7 +33,7 @@ jobs: uses: pion/.goassets/.github/workflows/test-i386.reusable.yml@master strategy: matrix: - go: ["1.23", "1.22"] # auto-update/supported-go-version-list + go: ["1.24", "1.23"] # auto-update/supported-go-version-list fail-fast: false with: go-version: ${{ matrix.go }} @@ -41,5 +41,5 @@ jobs: test-wasm: uses: pion/.goassets/.github/workflows/test-wasm.reusable.yml@master with: - go-version: "1.23" # auto-update/latest-go-version + go-version: "1.24" # auto-update/latest-go-version secrets: inherit diff --git a/.github/workflows/tidy-check.yaml b/.github/workflows/tidy-check.yaml index 417e730..710dbc9 100644 --- a/.github/workflows/tidy-check.yaml +++ b/.github/workflows/tidy-check.yaml @@ -22,4 +22,4 @@ jobs: tidy: uses: pion/.goassets/.github/workflows/tidy-check.reusable.yml@master with: - go-version: "1.22" # auto-update/latest-go-version + go-version: "1.24" # auto-update/latest-go-version diff --git a/.reuse/dep5 b/.reuse/dep5 index eb7fac2..4ce0569 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -6,6 +6,6 @@ Files: README.md DESIGN.md **/README.md AUTHORS.txt renovate.json go.mod go.sum Copyright: 2023 The Pion community License: MIT -Files: testdata/fuzz/* **/testdata/fuzz/* api/*.txt +Files: testdata/seed/* testdata/fuzz/* **/testdata/fuzz/* api/*.txt Copyright: 2023 The Pion community License: CC0-1.0 From bbb9792ca99d3ddba506b85a204b22917be8cadc Mon Sep 17 00:00:00 2001 From: Xiaobo Liu Date: Sat, 14 Jun 2025 10:47:36 +0800 Subject: [PATCH 101/114] Change activeTCPConn.close to atomic.Bool Replace manual atomic operations with atomic.Bool type for better type safety and cleaner code. This modernizes the atomic usage pattern from atomic.LoadInt32/StoreInt32 to the newer Load/Store methods on atomic.Bool. - Update activeTCPConn.closed field type from int32 to atomic.Bool - Replace atomic.LoadInt32(&a.closed) with a.closed.Load() - Replace atomic.StoreInt32(&a.closed, 1) with a.closed.Store(true) All existing functionality preserved with improved type safety. Signed-off-by: Xiaobo Liu tweak Signed-off-by: Xiaobo Liu --- active_tcp.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/active_tcp.go b/active_tcp.go index b55e650..2fd466b 100644 --- a/active_tcp.go +++ b/active_tcp.go @@ -18,7 +18,7 @@ import ( type activeTCPConn struct { readBuffer, writeBuffer *packetio.Buffer localAddr, remoteAddr atomic.Value - closed int32 + closed atomic.Bool } func newActiveTCPConn( @@ -34,7 +34,7 @@ func newActiveTCPConn( laddr, err := getTCPAddrOnInterface(localAddress) if err != nil { - atomic.StoreInt32(&a.closed, 1) + a.closed.Store(true) log.Infof("Failed to dial TCP address %s: %v", remoteAddress, err) return a @@ -43,7 +43,7 @@ func newActiveTCPConn( go func() { defer func() { - atomic.StoreInt32(&a.closed, 1) + a.closed.Store(true) }() dialer := &net.Dialer{ @@ -60,7 +60,7 @@ func newActiveTCPConn( go func() { buff := make([]byte, receiveMTU) - for atomic.LoadInt32(&a.closed) == 0 { + for !a.closed.Load() { n, err := readStreamingPacket(conn, buff) if err != nil { log.Infof("Failed to read streaming packet: %s", err) @@ -78,7 +78,7 @@ func newActiveTCPConn( buff := make([]byte, receiveMTU) - for atomic.LoadInt32(&a.closed) == 0 { + for !a.closed.Load() { n, err := a.writeBuffer.Read(buff) if err != nil { log.Infof("Failed to read from buffer: %s", err) @@ -102,7 +102,7 @@ func newActiveTCPConn( } func (a *activeTCPConn) ReadFrom(buff []byte) (n int, srcAddr net.Addr, err error) { - if atomic.LoadInt32(&a.closed) == 1 { + if a.closed.Load() { return 0, nil, io.ErrClosedPipe } @@ -114,7 +114,7 @@ func (a *activeTCPConn) ReadFrom(buff []byte) (n int, srcAddr net.Addr, err erro } func (a *activeTCPConn) WriteTo(buff []byte, _ net.Addr) (n int, err error) { - if atomic.LoadInt32(&a.closed) == 1 { + if a.closed.Load() { return 0, io.ErrClosedPipe } @@ -122,7 +122,7 @@ func (a *activeTCPConn) WriteTo(buff []byte, _ net.Addr) (n int, err error) { } func (a *activeTCPConn) Close() error { - atomic.StoreInt32(&a.closed, 1) + a.closed.Store(true) _ = a.readBuffer.Close() _ = a.writeBuffer.Close() From eef8d96d368c0400fabe93ea21e4bf3c97784695 Mon Sep 17 00:00:00 2001 From: Xiaobo Liu Date: Sat, 14 Jun 2025 16:43:59 +0800 Subject: [PATCH 102/114] Replace interface{} with any type alias This change maintains full backward compatibility while adopting modern Go type alias conventions for better code clarity. Signed-off-by: Xiaobo Liu --- active_tcp_test.go | 4 ++-- agent_test.go | 28 ++++++++++++++-------------- candidate_base.go | 4 ++-- gather.go | 2 +- internal/taskloop/taskloop.go | 2 +- udp_mux.go | 2 +- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/active_tcp_test.go b/active_tcp_test.go index b47ff6a..4959976 100644 --- a/active_tcp_test.go +++ b/active_tcp_test.go @@ -206,7 +206,7 @@ func TestActiveTCP_NonBlocking(t *testing.T) { require.NoError(t, bAgent.Close()) }() - isConnected := make(chan interface{}) + isConnected := make(chan any) err = aAgent.OnConnectionStateChange(func(c ConnectionState) { if c == ConnectionStateConnected { close(isConnected) @@ -269,7 +269,7 @@ func TestActiveTCP_Respect_NetworkTypes(t *testing.T) { require.NoError(t, bAgent.Close()) }() - isConnected := make(chan interface{}) + isConnected := make(chan any) err = aAgent.OnConnectionStateChange(func(c ConnectionState) { if c == ConnectionStateConnected { close(isConnected) diff --git a/agent_test.go b/agent_test.go index 0d97ea6..be86e29 100644 --- a/agent_test.go +++ b/agent_test.go @@ -505,7 +505,7 @@ func TestConnectionStateCallback(t *testing.T) { //nolint:cyclop InterfaceFilter: problematicNetworkInterfaces, } - isClosed := make(chan interface{}) + isClosed := make(chan any) aAgent, err := NewAgent(cfg) require.NoError(t, err) @@ -529,10 +529,10 @@ func TestConnectionStateCallback(t *testing.T) { //nolint:cyclop require.NoError(t, bAgent.Close()) }() - isChecking := make(chan interface{}) - isConnected := make(chan interface{}) - isDisconnected := make(chan interface{}) - isFailed := make(chan interface{}) + isChecking := make(chan any) + isConnected := make(chan any) + isDisconnected := make(chan any) + isFailed := make(chan any) err = aAgent.OnConnectionStateChange(func(c ConnectionState) { switch c { case ConnectionStateChecking: @@ -1058,7 +1058,7 @@ func TestConnectionStateFailedDeleteAllCandidates(t *testing.T) { require.NoError(t, bAgent.Close()) }() - isFailed := make(chan interface{}) + isFailed := make(chan any) require.NoError(t, aAgent.OnConnectionStateChange(func(c ConnectionState) { if c == ConnectionStateFailed { close(isFailed) @@ -1364,8 +1364,8 @@ func TestCloseInConnectionStateCallback(t *testing.T) { require.NoError(t, bAgent.Close()) }() - isClosed := make(chan interface{}) - isConnected := make(chan interface{}) + isClosed := make(chan any) + isConnected := make(chan any) err = aAgent.OnConnectionStateChange(func(c ConnectionState) { switch c { case ConnectionStateConnected: @@ -1414,7 +1414,7 @@ func TestRunTaskInConnectionStateCallback(t *testing.T) { require.NoError(t, bAgent.Close()) }() - isComplete := make(chan interface{}) + isComplete := make(chan any) err = aAgent.OnConnectionStateChange(func(c ConnectionState) { if c == ConnectionStateConnected { _, _, errCred := aAgent.GetLocalUserCredentials() @@ -1459,8 +1459,8 @@ func TestRunTaskInSelectedCandidatePairChangeCallback(t *testing.T) { require.NoError(t, bAgent.Close()) }() - isComplete := make(chan interface{}) - isTested := make(chan interface{}) + isComplete := make(chan any) + isTested := make(chan any) err = aAgent.OnSelectedCandidatePairChange(func(Candidate, Candidate) { go func() { _, _, errCred := aAgent.GetLocalUserCredentials() @@ -1528,9 +1528,9 @@ func TestLiteLifecycle(t *testing.T) { require.NoError(t, bAgent.Close()) }() - bConnected := make(chan interface{}) - bDisconnected := make(chan interface{}) - bFailed := make(chan interface{}) + bConnected := make(chan any) + bDisconnected := make(chan any) + bFailed := make(chan any) require.NoError(t, bAgent.OnConnectionStateChange(func(c ConnectionState) { switch c { diff --git a/candidate_base.go b/candidate_base.go index 66fb776..b36f7e4 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -69,7 +69,7 @@ func (c *candidateBase) Deadline() (deadline time.Time, ok bool) { } // Value implements context.Context. -func (c *candidateBase) Value(interface{}) interface{} { +func (c *candidateBase) Value(any) any { return nil } @@ -217,7 +217,7 @@ func (c *candidateBase) start(a *Agent, conn net.PacketConn, initializedCh <-cha } var bufferPool = sync.Pool{ // nolint:gochecknoglobals - New: func() interface{} { + New: func() any { return make([]byte, receiveMTU) }, } diff --git a/gather.go b/gather.go index a1e0247..ebf2999 100644 --- a/gather.go +++ b/gather.go @@ -22,7 +22,7 @@ import ( ) // Close a net.Conn and log if we have a failure. -func closeConnAndLog(c io.Closer, log logging.LeveledLogger, msg string, args ...interface{}) { +func closeConnAndLog(c io.Closer, log logging.LeveledLogger, msg string, args ...any) { if c == nil || (reflect.ValueOf(c).Kind() == reflect.Ptr && reflect.ValueOf(c).IsNil()) { log.Warnf("Connection is not allocated: "+msg, args...) diff --git a/internal/taskloop/taskloop.go b/internal/taskloop/taskloop.go index 15a2666..63e780f 100644 --- a/internal/taskloop/taskloop.go +++ b/internal/taskloop/taskloop.go @@ -116,6 +116,6 @@ func (l *Loop) Deadline() (deadline time.Time, ok bool) { } // Value is not supported for task loops. -func (l *Loop) Value(interface{}) interface{} { +func (l *Loop) Value(any) any { return nil } diff --git a/udp_mux.go b/udp_mux.go index 3732b26..48f65aa 100644 --- a/udp_mux.go +++ b/udp_mux.go @@ -116,7 +116,7 @@ func NewUDPMuxDefault(params UDPMuxParams) *UDPMuxDefault { //nolint:cyclop connsIPv6: make(map[string]*udpMuxedConn), closedChan: make(chan struct{}, 1), pool: &sync.Pool{ - New: func() interface{} { + New: func() any { // Big enough buffer to fit both packet and address return newBufferHolder(receiveMTU) }, From 613ac5a204ee7502be5a99291c9e3473988c6e89 Mon Sep 17 00:00:00 2001 From: Xiaobo Liu Date: Sun, 15 Jun 2025 11:18:48 +0800 Subject: [PATCH 103/114] Use atomic.Uint64 for thread-safe byte counters Signed-off-by: Xiaobo Liu --- transport.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/transport.go b/transport.go index a28605d..81c2a0a 100644 --- a/transport.go +++ b/transport.go @@ -27,19 +27,19 @@ func (a *Agent) Accept(ctx context.Context, remoteUfrag, remotePwd string) (*Con // Conn represents the ICE connection. // At the moment the lifetime of the Conn is equal to the Agent. type Conn struct { - bytesReceived uint64 - bytesSent uint64 + bytesReceived atomic.Uint64 + bytesSent atomic.Uint64 agent *Agent } // BytesSent returns the number of bytes sent. func (c *Conn) BytesSent() uint64 { - return atomic.LoadUint64(&c.bytesSent) + return c.bytesSent.Load() } // BytesReceived returns the number of bytes received. func (c *Conn) BytesReceived() uint64 { - return atomic.LoadUint64(&c.bytesReceived) + return c.bytesReceived.Load() } func (a *Agent) connect(ctx context.Context, isControlling bool, remoteUfrag, remotePwd string) (*Conn, error) { @@ -74,7 +74,7 @@ func (c *Conn) Read(p []byte) (int, error) { } n, err := c.agent.buf.Read(p) - atomic.AddUint64(&c.bytesReceived, uint64(n)) //nolint:gosec // G115 + c.bytesReceived.Add(uint64(n)) //nolint:gosec // G115 return n, err } @@ -103,7 +103,7 @@ func (c *Conn) Write(packet []byte) (int, error) { } } - atomic.AddUint64(&c.bytesSent, uint64(len(packet))) + c.bytesSent.Add(uint64(len(packet))) return pair.Write(packet) } From 8930d1b7d0c3b0608eaa4add81fd884446dd6cdc Mon Sep 17 00:00:00 2001 From: Pion <59523206+pionbot@users.noreply.github.com> Date: Wed, 18 Jun 2025 07:42:56 +0000 Subject: [PATCH 104/114] Update CI configs to v0.11.20 Update lint scripts and CI configs. --- .golangci.yml | 6 ++++++ .reuse/dep5 | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index 120faf2..59edee2 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -41,6 +41,12 @@ linters-settings: - w io.Writer - r io.Reader - b []byte + revive: + rules: + # Prefer 'any' type alias over 'interface{}' for Go 1.18+ compatibility + - name: use-any + severity: warning + disabled: false linters: enable: diff --git a/.reuse/dep5 b/.reuse/dep5 index 4ce0569..b26c56d 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -2,7 +2,7 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Pion Source: https://github.com/pion/ -Files: README.md DESIGN.md **/README.md AUTHORS.txt renovate.json go.mod go.sum **/go.mod **/go.sum .eslintrc.json package.json examples.json sfu-ws/flutter/.gitignore sfu-ws/flutter/pubspec.yaml c-data-channels/webrtc.h examples/examples.json +Files: README.md DESIGN.md **/README.md AUTHORS.txt renovate.json go.mod go.sum **/go.mod **/go.sum .eslintrc.json package.json examples.json sfu-ws/flutter/.gitignore sfu-ws/flutter/pubspec.yaml c-data-channels/webrtc.h examples/examples.json yarn.lock Copyright: 2023 The Pion community License: MIT From b818e2cbcf5a90f37403b3c7a28f26d376059727 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 01:28:17 +0000 Subject: [PATCH 105/114] Update module github.com/pion/dtls/v3 to v3.0.6 Generated by renovateBot --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index d42aa67..a5d1e37 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/google/uuid v1.6.0 - github.com/pion/dtls/v3 v3.0.4 + github.com/pion/dtls/v3 v3.0.6 github.com/pion/logging v0.2.3 github.com/pion/mdns/v2 v2.0.7 github.com/pion/randutil v0.1.0 @@ -12,7 +12,7 @@ require ( github.com/pion/transport/v3 v3.0.7 github.com/pion/turn/v4 v4.0.0 github.com/stretchr/testify v1.10.0 - golang.org/x/net v0.33.0 + golang.org/x/net v0.34.0 ) require ( @@ -20,8 +20,8 @@ require ( github.com/kr/pretty v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/wlynxg/anet v0.0.3 // indirect - golang.org/x/crypto v0.31.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/sys v0.29.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 5431b28..38de92e 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U= -github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg= +github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E= +github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU= github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90= github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= @@ -27,12 +27,12 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg= github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 2a0a17ed3481c02b0a368f9e7c239ec484210151 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 08:51:33 +0000 Subject: [PATCH 106/114] Update module github.com/pion/turn/v4 to v4.0.2 Generated by renovateBot --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index a5d1e37..af3dcf8 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/pion/randutil v0.1.0 github.com/pion/stun/v3 v3.0.0 github.com/pion/transport/v3 v3.0.7 - github.com/pion/turn/v4 v4.0.0 + github.com/pion/turn/v4 v4.0.2 github.com/stretchr/testify v1.10.0 golang.org/x/net v0.34.0 ) @@ -21,7 +21,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/wlynxg/anet v0.0.3 // indirect golang.org/x/crypto v0.32.0 // indirect - golang.org/x/sys v0.29.0 // indirect + golang.org/x/sys v0.30.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 38de92e..1e973ac 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,8 @@ github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw= github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= -github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM= -github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA= +github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps= +github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= @@ -31,8 +31,8 @@ golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From cc019aabdf4e8703d73020674c2c09420451d55e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 16:14:38 +0000 Subject: [PATCH 107/114] Update module github.com/pion/logging to v0.2.4 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index af3dcf8..5deefe6 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/google/uuid v1.6.0 github.com/pion/dtls/v3 v3.0.6 - github.com/pion/logging v0.2.3 + github.com/pion/logging v0.2.4 github.com/pion/mdns/v2 v2.0.7 github.com/pion/randutil v0.1.0 github.com/pion/stun/v3 v3.0.0 diff --git a/go.sum b/go.sum index 1e973ac..7d7f3a1 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E= github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU= -github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= -github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90= +github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8= +github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so= github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= From f6a1153ce74696f884ef91577fb4fed4a559cbe1 Mon Sep 17 00:00:00 2001 From: Xiaobo Liu Date: Fri, 27 Jun 2025 18:20:26 +0800 Subject: [PATCH 108/114] Optimize slice allocation in UDPMuxDefault Pre-allocate localAddrsForUnspecified slice with known capacity and use index assignment instead of append to avoid multiple slice reallocations. Signed-off-by: Xiaobo Liu --- udp_mux.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/udp_mux.go b/udp_mux.go index 48f65aa..257ef5f 100644 --- a/udp_mux.go +++ b/udp_mux.go @@ -95,12 +95,13 @@ func NewUDPMuxDefault(params UDPMuxParams) *UDPMuxDefault { //nolint:cyclop _, addrs, err := localInterfaces(params.Net, nil, nil, networks, true) if err == nil { - for _, addr := range addrs { - localAddrsForUnspecified = append(localAddrsForUnspecified, &net.UDPAddr{ + localAddrsForUnspecified = make([]net.Addr, len(addrs)) + for i, addr := range addrs { + localAddrsForUnspecified[i] = &net.UDPAddr{ IP: addr.AsSlice(), Port: udpAddr.Port, Zone: addr.Zone(), - }) + } } } else { params.Logger.Errorf("Failed to get local interfaces for unspecified addr: %v", err) From 2c04474e3879511782d203e5c9d9ca6da60a20e5 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Wed, 16 Jul 2025 16:34:30 -0400 Subject: [PATCH 109/114] Implement ICE Role conflict resolution Detect if remote has a role conflict and resolve it as defined by RFC 8445 section-7.3.1.1 Resolves #359 --- agent.go | 135 +++++++++++++++++++++++++++++++++----------------- agent_test.go | 64 ++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 45 deletions(-) diff --git a/agent.go b/agent.go index 80c89d0..a7f090d 100644 --- a/agent.go +++ b/agent.go @@ -62,7 +62,7 @@ type Agent struct { muHaveStarted sync.Mutex startedCh <-chan struct{} startedFn func() - isControlling bool + isControlling atomic.Bool maxBindingRequests uint16 @@ -104,7 +104,9 @@ type Agent struct { remoteCandidates map[NetworkType][]Candidate checklist []*CandidatePair - selector pairCandidateSelector + + selectorLock sync.RWMutex + selector pairCandidateSelector selectedPair atomic.Value // *CandidatePair @@ -343,21 +345,11 @@ func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remoteP a.log.Debugf("Started agent: isControlling? %t, remoteUfrag: %q, remotePwd: %q", isControlling, remoteUfrag, remotePwd) return a.loop.Run(a.loop, func(_ context.Context) { - a.isControlling = isControlling + a.isControlling.Store(isControlling) a.remoteUfrag = remoteUfrag a.remotePwd = remotePwd + a.setSelector() - if isControlling { - a.selector = &controllingSelector{agent: a, log: a.log} - } else { - a.selector = &controlledSelector{agent: a, log: a.log} - } - - if a.lite { - a.selector = &liteSelector{pairCandidateSelector: a.selector} - } - - a.selector.Start() a.startedFn() a.updateConnectionState(ConnectionStateChecking) @@ -397,7 +389,7 @@ func (a *Agent) connectivityChecks() { //nolint:cyclop default: } - a.selector.ContactCandidates() + a.getSelector().ContactCandidates() }); err != nil { a.log.Warnf("Failed to start connectivity checks: %v", err) } @@ -501,7 +493,7 @@ func (a *Agent) pingAllCandidates() { a.log.Tracef("Maximum requests reached for pair %s, marking it as failed", p) p.state = CandidatePairStateFailed } else { - a.selector.PingCandidate(p.Local, p.Remote) + a.getSelector().PingCandidate(p.Local, p.Remote) p.bindingRequestCount++ } } @@ -542,7 +534,7 @@ func (a *Agent) getBestValidCandidatePair() *CandidatePair { } func (a *Agent) addPair(local, remote Candidate) *CandidatePair { - p := newCandidatePair(local, remote, a.isControlling) + p := newCandidatePair(local, remote, a.isControlling.Load()) a.checklist = append(a.checklist, p) return p @@ -598,7 +590,7 @@ func (a *Agent) checkKeepalive() { if a.keepaliveInterval != 0 { // We use binding request instead of indication to support refresh consent schemas // see https://tools.ietf.org/html/rfc7675 - a.selector.PingCandidate(selectedPair.Local, selectedPair.Remote) + a.getSelector().PingCandidate(selectedPair.Local, selectedPair.Remote) } } @@ -1064,9 +1056,39 @@ func (a *Agent) handleInboundBindingSuccess(id [stun.TransactionIDSize]byte) (bo return false, nil, 0 } +func (a *Agent) handleRoleConflict(msg *stun.Message, local, remote Candidate, remoteTieBreaker *AttrControl) { + localIsGreaterOrEqual := a.tieBreaker >= remoteTieBreaker.Tiebreaker + a.log.Warnf("Role conflict local and remote same role(%s), localIsGreaterOrEqual(%t)", a.role(), localIsGreaterOrEqual) + + // https://datatracker.ietf.org/doc/html/rfc8445#section-7.3.1.1 + // An agent MUST examine the Binding request for either the ICE- + // CONTROLLING or ICE-CONTROLLED attribute. It MUST follow these + // procedures: + + // If the agent's tiebreaker value is larger than or equal to the contents of the ICE-CONTROLLING attribute + // If the agent's tiebreaker value is less than the contents of the ICE-CONTROLLED attribute + // the agent generates a Binding error response + if (a.isControlling.Load() && localIsGreaterOrEqual) || (!a.isControlling.Load() && !localIsGreaterOrEqual) { + if roleConflictMsg, err := stun.Build(msg, stun.BindingError, + stun.ErrorCodeAttribute{ + Code: stun.CodeRoleConflict, + Reason: []byte("Role Conflict"), + }, + stun.NewShortTermIntegrity(a.localPwd), + stun.Fingerprint, + ); err != nil { + a.log.Warnf("Failed to generate Role Conflict message from: %s to: %s error: %s", local, remote, err) + } else { + a.sendSTUN(roleConflictMsg, local, remote) + } + } else { + a.isControlling.Store(!a.isControlling.Load()) + a.setSelector() + } +} + // handleInbound processes STUN traffic from a remote candidate. func (a *Agent) handleInbound(msg *stun.Message, local Candidate, remote net.Addr) { //nolint:gocognit,cyclop - var err error if msg == nil || local == nil { return } @@ -1080,27 +1102,10 @@ func (a *Agent) handleInbound(msg *stun.Message, local Candidate, remote net.Add return } - if a.isControlling { - if msg.Contains(stun.AttrICEControlling) { - a.log.Debug("Inbound STUN message: isControlling && a.isControlling == true") - - return - } else if msg.Contains(stun.AttrUseCandidate) { - a.log.Debug("Inbound STUN message: useCandidate && a.isControlling == true") - - return - } - } else { - if msg.Contains(stun.AttrICEControlled) { - a.log.Debug("Inbound STUN message: isControlled && a.isControlling == false") - - return - } - } - remoteCandidate := a.findRemoteCandidate(local.NetworkType(), remote) + if msg.Type.Class == stun.ClassSuccessResponse { //nolint:nestif - if err = stun.MessageIntegrity([]byte(a.remotePwd)).Check(msg); err != nil { + if err := stun.MessageIntegrity([]byte(a.remotePwd)).Check(msg); err != nil { a.log.Warnf("Discard message from (%s), %v", remote, err) return @@ -1112,7 +1117,7 @@ func (a *Agent) handleInbound(msg *stun.Message, local Candidate, remote net.Add return } - a.selector.HandleSuccessResponse(msg, local, remoteCandidate, remote) + a.getSelector().HandleSuccessResponse(msg, local, remoteCandidate, remote) } else if msg.Type.Class == stun.ClassRequest { a.log.Tracef( "Inbound STUN (Request) from %s to %s, useCandidate: %v", @@ -1121,11 +1126,11 @@ func (a *Agent) handleInbound(msg *stun.Message, local Candidate, remote net.Add msg.Contains(stun.AttrUseCandidate), ) - if err = stunx.AssertUsername(msg, a.localUfrag+":"+a.remoteUfrag); err != nil { + if err := stunx.AssertUsername(msg, a.localUfrag+":"+a.remoteUfrag); err != nil { a.log.Warnf("Discard message from (%s), %v", remote, err) return - } else if err = stun.MessageIntegrity([]byte(a.localPwd)).Check(msg); err != nil { + } else if err := stun.MessageIntegrity([]byte(a.localPwd)).Check(msg); err != nil { a.log.Warnf("Discard message from (%s), %v", remote, err) return @@ -1160,7 +1165,16 @@ func (a *Agent) handleInbound(msg *stun.Message, local Candidate, remote net.Add a.addRemoteCandidate(remoteCandidate) } - a.selector.HandleBindingRequest(msg, local, remoteCandidate) + // Support Remotes that don't set a TIE-BREAKER. Not standards compliant, but + // keeping to maintain backwards compat + remoteTieBreaker := &AttrControl{} + if err := remoteTieBreaker.GetFrom(msg); err == nil && remoteTieBreaker.Role == a.role() { + a.handleRoleConflict(msg, local, remoteCandidate, remoteTieBreaker) + + return + } + + a.getSelector().HandleBindingRequest(msg, local, remoteCandidate) } if remoteCandidate != nil { @@ -1282,9 +1296,7 @@ func (a *Agent) Restart(ufrag, pwd string) error { //nolint:cyclop a.pendingBindingRequests = make([]bindingRequest, 0) a.setSelectedPair(nil) a.deleteAllCandidates() - if a.selector != nil { - a.selector.Start() - } + a.setSelector() // Restart is used by NewAgent. Accept/Connect should be used to move to checking // for new Agents @@ -1319,3 +1331,36 @@ func (a *Agent) setGatheringState(newState GatheringState) error { func (a *Agent) needsToCheckPriorityOnNominated() bool { return !a.lite || a.enableUseCandidateCheckPriority } + +func (a *Agent) role() Role { + if a.isControlling.Load() { + return Controlling + } + + return Controlled +} + +func (a *Agent) setSelector() { + a.selectorLock.Lock() + defer a.selectorLock.Unlock() + + var s pairCandidateSelector + if a.isControlling.Load() { + s = &controllingSelector{agent: a, log: a.log} + } else { + s = &controlledSelector{agent: a, log: a.log} + } + if a.lite { + s = &liteSelector{pairCandidateSelector: s} + } + + s.Start() + a.selector = s +} + +func (a *Agent) getSelector() pairCandidateSelector { + a.selectorLock.Lock() + defer a.selectorLock.Unlock() + + return a.selector +} diff --git a/agent_test.go b/agent_test.go index be86e29..29f99d2 100644 --- a/agent_test.go +++ b/agent_test.go @@ -1947,3 +1947,67 @@ func TestAlwaysSentKeepAlive(t *testing.T) { //nolint:cyclop newLastSent = pair.Local.LastSent() require.NotEqual(t, lastSent, newLastSent) } + +func TestRoleConflict(t *testing.T) { + defer test.CheckRoutines(t)() + defer test.TimeOut(time.Second * 30).Stop() + + runTest := func(doDial bool) { + cfg := &AgentConfig{ + NetworkTypes: supportedNetworkTypes(), + MulticastDNSMode: MulticastDNSModeDisabled, + InterfaceFilter: problematicNetworkInterfaces, + } + + aAgent, err := NewAgent(cfg) + require.NoError(t, err) + + bAgent, err := NewAgent(cfg) + require.NoError(t, err) + + isConnected := make(chan any) + err = aAgent.OnConnectionStateChange(func(c ConnectionState) { + if c == ConnectionStateConnected { + close(isConnected) + } + }) + require.NoError(t, err) + + gatherAndExchangeCandidates(t, aAgent, bAgent) + + go func() { + ufrag, pwd, routineErr := bAgent.GetLocalUserCredentials() + require.NoError(t, routineErr) + + if doDial { + _, routineErr = aAgent.Dial(context.TODO(), ufrag, pwd) + } else { + _, routineErr = aAgent.Accept(context.TODO(), ufrag, pwd) + } + require.NoError(t, routineErr) + }() + + ufrag, pwd, err := aAgent.GetLocalUserCredentials() + require.NoError(t, err) + + if doDial { + _, err = bAgent.Dial(context.TODO(), ufrag, pwd) + } else { + _, err = bAgent.Accept(context.TODO(), ufrag, pwd) + } + require.NoError(t, err) + + <-isConnected + + require.NoError(t, aAgent.Close()) + require.NoError(t, bAgent.Close()) + } + + t.Run("Controlling", func(t *testing.T) { + runTest(true) + }) + + t.Run("Controlled", func(t *testing.T) { + runTest(false) + }) +} From 1d2f139ce3a1bf1eaad0adba3d20b75ce9a18289 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:43:41 +0000 Subject: [PATCH 110/114] Update module github.com/pion/turn/v4 to v4.1.0 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5deefe6..be06bda 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/pion/randutil v0.1.0 github.com/pion/stun/v3 v3.0.0 github.com/pion/transport/v3 v3.0.7 - github.com/pion/turn/v4 v4.0.2 + github.com/pion/turn/v4 v4.1.0 github.com/stretchr/testify v1.10.0 golang.org/x/net v0.34.0 ) diff --git a/go.sum b/go.sum index 7d7f3a1..2b54d8e 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,8 @@ github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw= github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= -github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps= -github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs= +github.com/pion/turn/v4 v4.1.0 h1:+J56+aS8Bi6B4zij3ah6VvJpRuy8W8FtExR0OJPiTdM= +github.com/pion/turn/v4 v4.1.0/go.mod h1:2123tHk1O++vmjI5VSD0awT50NywDAq5A2NNNU4Jjs8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= From 0b78af930158e0b6d1bb07e36ee0caff3e5153d1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 16 Aug 2025 14:38:44 +0000 Subject: [PATCH 111/114] Update module github.com/pion/dtls/v3 to v3.0.7 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index be06bda..248d00d 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/google/uuid v1.6.0 - github.com/pion/dtls/v3 v3.0.6 + github.com/pion/dtls/v3 v3.0.7 github.com/pion/logging v0.2.4 github.com/pion/mdns/v2 v2.0.7 github.com/pion/randutil v0.1.0 diff --git a/go.sum b/go.sum index 2b54d8e..93bd74c 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E= -github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU= +github.com/pion/dtls/v3 v3.0.7 h1:bItXtTYYhZwkPFk4t1n3Kkf5TDrfj6+4wG+CZR8uI9Q= +github.com/pion/dtls/v3 v3.0.7/go.mod h1:uDlH5VPrgOQIw59irKYkMudSFprY9IEFCqz/eTz16f8= github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8= github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so= github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= From e13ec5227813da1c785d0b0ad985216efff23cb6 Mon Sep 17 00:00:00 2001 From: Pion <59523206+pionbot@users.noreply.github.com> Date: Sat, 16 Aug 2025 14:52:33 +0000 Subject: [PATCH 112/114] Update CI configs to v0.11.22 Update lint scripts and CI configs. --- .golangci.yml | 146 +++++++++++++++++++++++++------------------------- 1 file changed, 72 insertions(+), 74 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 59edee2..6fddc53 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,53 +1,7 @@ # SPDX-FileCopyrightText: 2023 The Pion community # SPDX-License-Identifier: MIT -run: - timeout: 5m - -linters-settings: - govet: - enable: - - shadow - misspell: - locale: US - exhaustive: - default-signifies-exhaustive: true - gomodguard: - blocked: - modules: - - github.com/pkg/errors: - recommendations: - - errors - forbidigo: - analyze-types: true - forbid: - - ^fmt.Print(f|ln)?$ - - ^log.(Panic|Fatal|Print)(f|ln)?$ - - ^os.Exit$ - - ^panic$ - - ^print(ln)?$ - - p: ^testing.T.(Error|Errorf|Fatal|Fatalf|Fail|FailNow)$ - pkg: ^testing$ - msg: "use testify/assert instead" - varnamelen: - max-distance: 12 - min-name-length: 2 - ignore-type-assert-ok: true - ignore-map-index-ok: true - ignore-chan-recv-ok: true - ignore-decls: - - i int - - n int - - w io.Writer - - r io.Reader - - b []byte - revive: - rules: - # Prefer 'any' type alias over 'interface{}' for Go 1.18+ compatibility - - name: use-any - severity: warning - disabled: false - +version: "2" linters: enable: - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers @@ -66,10 +20,8 @@ linters: - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. - exhaustive # check exhaustiveness of enum switch statements - - exportloopref # checks for pointers to enclosing loop variables - forbidigo # Forbids identifiers - forcetypeassert # finds forced type assertions - - gci # Gci control golang package import order and make it always deterministic. - gochecknoglobals # Checks that no globals are present in Go code - gocognit # Computes and checks the cognitive complexity of functions - goconst # Finds repeated strings that could be replaced by a constant @@ -77,14 +29,10 @@ linters: - gocyclo # Computes and checks the cyclomatic complexity of functions - godot # Check if comments end in a period - godox # Tool for detection of FIXME, TODO and other comment keywords - - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification - - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern - - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. - goprintffuncname # Checks that printf-like functions are named with `f` at the end - gosec # Inspects source code for security problems - - gosimple # Linter for Go source code that specializes in simplifying a code - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string - grouper # An analyzer to analyze expression groups. - importas # Enforces consistent import aliases @@ -102,11 +50,8 @@ linters: - predeclared # find code that shadows one of Go's predeclared identifiers - revive # golint replacement, finds style mistakes - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks - - stylecheck # Stylecheck is a replacement for golint - tagliatelle # Checks the struct tags. - - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers - - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code - unconvert # Remove unnecessary type conversions - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types @@ -131,21 +76,74 @@ linters: - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes - wrapcheck # Checks that errors returned from external packages are wrapped - wsl # Whitespace Linter - Forces you to use empty lines! - -issues: - exclude-use-default: false - exclude-dirs-use-default: false - exclude-rules: - # Allow complex tests and examples, better to be self contained - - path: (examples|main\.go) - linters: - - gocognit - - forbidigo - - path: _test\.go - linters: - - gocognit - - # Allow forbidden identifiers in CLI commands - - path: cmd - linters: - - forbidigo + settings: + staticcheck: + checks: + - all + # "could remove embedded field", to keep it explicit! + - -QF1008 + # "could use tagged switch on enum", Cases conflicts with exhaustive! + - -QF1003 + exhaustive: + default-signifies-exhaustive: true + forbidigo: + forbid: + - pattern: ^fmt.Print(f|ln)?$ + - pattern: ^log.(Panic|Fatal|Print)(f|ln)?$ + - pattern: ^os.Exit$ + - pattern: ^panic$ + - pattern: ^print(ln)?$ + - pattern: ^testing.T.(Error|Errorf|Fatal|Fatalf|Fail|FailNow)$ + pkg: ^testing$ + msg: use testify/assert instead + analyze-types: true + gomodguard: + blocked: + modules: + - github.com/pkg/errors: + recommendations: + - errors + govet: + enable: + - shadow + revive: + rules: + # Prefer 'any' type alias over 'interface{}' for Go 1.18+ compatibility + - name: use-any + severity: warning + disabled: false + misspell: + locale: US + varnamelen: + max-distance: 12 + min-name-length: 2 + ignore-type-assert-ok: true + ignore-map-index-ok: true + ignore-chan-recv-ok: true + ignore-decls: + - i int + - n int + - w io.Writer + - r io.Reader + - b []byte + exclusions: + generated: lax + rules: + - linters: + - forbidigo + - gocognit + path: (examples|main\.go) + - linters: + - gocognit + path: _test\.go + - linters: + - forbidigo + path: cmd +formatters: + enable: + - gci + - gofmt + - gofumpt + - goimports + exclusions: + generated: lax From be0657f17b7a375ece0f6e9382ab6d9a5105cae4 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Sat, 16 Aug 2025 23:32:49 -0400 Subject: [PATCH 113/114] Fix lint errors --- agent.go | 6 +++--- candidate_base.go | 18 +++++++++--------- tcp_packet_conn.go | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/agent.go b/agent.go index a7f090d..5220c1d 100644 --- a/agent.go +++ b/agent.go @@ -1094,9 +1094,9 @@ func (a *Agent) handleInbound(msg *stun.Message, local Candidate, remote net.Add } if msg.Type.Method != stun.MethodBinding || - !(msg.Type.Class == stun.ClassSuccessResponse || - msg.Type.Class == stun.ClassRequest || - msg.Type.Class == stun.ClassIndication) { + (msg.Type.Class != stun.ClassSuccessResponse && + msg.Type.Class != stun.ClassRequest && + msg.Type.Class != stun.ClassIndication) { a.log.Tracef("Unhandled STUN from %s to %s class(%s) method(%s)", remote, local, msg.Type.Class, msg.Type.Method) return diff --git a/candidate_base.go b/candidate_base.go index b36f7e4..45c090e 100644 --- a/candidate_base.go +++ b/candidate_base.go @@ -243,7 +243,7 @@ func (c *candidateBase) recvLoop(initializedCh <-chan struct{}) { for { n, srcAddr, err := c.conn.ReadFrom(buf) if err != nil { - if !(errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed)) { + if !errors.Is(err, io.EOF) && !errors.Is(err, net.ErrClosed) { agent.log.Warnf("Failed to read from candidate %s: %v", c, err) } @@ -891,10 +891,10 @@ func readCandidateCharToken(raw string, start int, limit int) (string, int, erro return "", 0, fmt.Errorf("token too long: %s expected 1x%d", raw[start:start+i], limit) } - if !(char >= 'A' && char <= 'Z' || - char >= 'a' && char <= 'z' || - char >= '0' && char <= '9' || - char == '+' || char == '/') { + if (char < 'A' || char > 'Z') && + (char < 'a' || char > 'z') && + (char < '0' || char > '9') && + char != '+' && char != '/' { return "", 0, fmt.Errorf("invalid ice-char token: %c", char) //nolint: err113 // handled by caller } } @@ -928,7 +928,7 @@ func readCandidateDigitToken(raw string, start, limit int) (int, int, error) { return 0, 0, fmt.Errorf("token too long: %s expected 1x%d", raw[start:start+i], limit) } - if !(char >= '0' && char <= '9') { + if char < '0' || char > '9' { return 0, 0, fmt.Errorf("invalid digit token: %c", char) //nolint: err113 // handled by caller } @@ -962,9 +962,9 @@ func readCandidateByteString(raw string, start int) (string, int, error) { } // 1*(%x01-09/%x0B-0C/%x0E-FF) - if !(char >= 0x01 && char <= 0x09 || - char >= 0x0B && char <= 0x0C || - char >= 0x0E && char <= 0xFF) { + if (char < 0x01 || char > 0x09) && + (char < 0x0B || char > 0x0C) && + (char < 0x0E || char > 0xFF) { return "", 0, fmt.Errorf("invalid byte-string character: %c", char) //nolint: err113 // handled by caller } } diff --git a/tcp_packet_conn.go b/tcp_packet_conn.go index 1d1dffc..b7e7162 100644 --- a/tcp_packet_conn.go +++ b/tcp_packet_conn.go @@ -190,7 +190,7 @@ func (t *tcpPacketConn) startReading(conn net.Conn) { t.params.Logger.Warnf("Failed to read streaming packet: %s", err) last := t.removeConn(conn) // Only propagate connection closure errors if no other open connection exists. - if last || !(errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed)) { + if last || (!errors.Is(err, io.EOF) && !errors.Is(err, net.ErrClosed)) { t.handleRecv(streamingPacket{nil, conn.RemoteAddr(), err}) } From 3d59690a71bed0ac9ee2fc4ad81905cf3776047d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 17 Aug 2025 08:51:12 +0000 Subject: [PATCH 114/114] Update module github.com/pion/turn/v4 to v4.1.1 Generated by renovateBot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 248d00d..7b30346 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/pion/randutil v0.1.0 github.com/pion/stun/v3 v3.0.0 github.com/pion/transport/v3 v3.0.7 - github.com/pion/turn/v4 v4.1.0 + github.com/pion/turn/v4 v4.1.1 github.com/stretchr/testify v1.10.0 golang.org/x/net v0.34.0 ) diff --git a/go.sum b/go.sum index 93bd74c..5d020c8 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,8 @@ github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw= github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= -github.com/pion/turn/v4 v4.1.0 h1:+J56+aS8Bi6B4zij3ah6VvJpRuy8W8FtExR0OJPiTdM= -github.com/pion/turn/v4 v4.1.0/go.mod h1:2123tHk1O++vmjI5VSD0awT50NywDAq5A2NNNU4Jjs8= +github.com/pion/turn/v4 v4.1.1 h1:9UnY2HB99tpDyz3cVVZguSxcqkJ1DsTSZ+8TGruh4fc= +github.com/pion/turn/v4 v4.1.1/go.mod h1:2123tHk1O++vmjI5VSD0awT50NywDAq5A2NNNU4Jjs8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=