Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion selection.go
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,15 @@ func (s *controlledSelector) HandleSuccessResponse(m *stun.Message, local, remot
}

func (s *controlledSelector) HandleBindingRequest(message *stun.Message, local, remote Candidate) { //nolint:cyclop
s.handleBindingRequest(message, local, remote, true)
}

//nolint:cyclop
func (s *controlledSelector) handleBindingRequest(
message *stun.Message,
local, remote Candidate,
pingCandidate bool,
) {
pair := s.agent.findPair(local, remote)
if pair == nil {
pair = s.agent.addPair(local, remote)
Expand Down Expand Up @@ -481,7 +490,7 @@ func (s *controlledSelector) HandleBindingRequest(message *stun.Message, local,
// on every inbound request creates a ping-pong busy loop: the remote side responds
// and sends its own request, which triggers another check here, repeating at 1/RTT.
// After connection, consent freshness is maintained by checkKeepalive() on a timer.
if pair.state != CandidatePairStateSucceeded || s.agent.getSelectedPair() == nil {
if pingCandidate && (pair.state != CandidatePairStateSucceeded || s.agent.getSelectedPair() == nil) {
s.PingCandidate(local, remote)
}

Expand All @@ -496,6 +505,25 @@ type liteSelector struct {
pairCandidateSelector
}

// A lite selector only acts as a STUN server, so it responds to Binding
// Requests but does not generate triggered checks of its own.
func (s *liteSelector) HandleBindingRequest(message *stun.Message, local, remote Candidate) {
if v, ok := s.pairCandidateSelector.(*controlledSelector); ok {
if message.Contains(stun.AttrUseCandidate) || message.Contains(v.agent.nominationAttribute) {
pair := v.agent.findPair(local, remote)
if pair == nil {
pair = v.agent.addPair(local, remote)
}
pair.state = CandidatePairStateSucceeded
}
v.handleBindingRequest(message, local, remote, false)

return
}

s.pairCandidateSelector.HandleBindingRequest(message, local, remote)
}

// A lite selector should not contact candidates.
func (s *liteSelector) ContactCandidates() {
if _, ok := s.pairCandidateSelector.(*controllingSelector); ok {
Expand Down
41 changes: 41 additions & 0 deletions selection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,47 @@ func TestControlledSelector_TriggeredCheckDuringChecking(t *testing.T) {
"triggered check should be sent during ICE checking phase")
}

func TestLiteSelector_NoTriggeredCheckForAcceptedNomination(t *testing.T) {
agent := bareAgentForPing()
agent.log = logging.NewDefaultLoggerFactory().NewLogger("test")
agent.remoteUfrag = selectionTestRemoteUfrag
agent.localUfrag = selectionTestLocalUfrag
agent.remotePwd = selectionTestPassword
agent.localPwd = selectionTestPassword
agent.tieBreaker = 1
agent.nominationAttribute = DefaultNominationAttribute
agent.onConnected = make(chan struct{})

selector := &liteSelector{pairCandidateSelector: &controlledSelector{agent: agent, log: agent.log}}
selector.Start()

local := newPingNoIOCand()
local.candidateBase.networkType = NetworkTypeUDP4
local.candidateBase.resolvedAddr = &net.UDPAddr{IP: net.ParseIP("192.168.1.1"), Port: 10000}

remote := newPingNoIOCand()
remote.candidateBase.networkType = NetworkTypeUDP4
remote.candidateBase.resolvedAddr = &net.UDPAddr{IP: net.ParseIP("192.168.1.2"), Port: 20000}

msg, err := stun.Build(
stun.BindingRequest,
stun.TransactionID,
stun.NewUsername(agent.localUfrag+":"+agent.remoteUfrag),
UseCandidate(),
stun.NewShortTermIntegrity(agent.localPwd),
stun.Fingerprint,
)
require.NoError(t, err)

selector.HandleBindingRequest(msg, local, remote)

pair := agent.findPair(local, remote)
require.NotNil(t, pair)
assert.Equal(t, CandidatePairStateSucceeded, pair.state)
assert.Equal(t, pair, agent.getSelectedPair())
assert.Zero(t, pair.RequestsSent(), "lite agent should not send a triggered check")
}

func TestAutomaticRenomination(t *testing.T) { //nolint:maintidx
report := test.CheckRoutines(t)
defer report()
Expand Down
Loading