Skip to content

Commit 953f36f

Browse files
seppo0010Sean-Der
authored andcommitted
Add config to run Agent in Lite mode
Allow the agent to run in Lite mode. This is useful in cases where you never want connectivity checks, and reduces possible attacks surfaces when you have a public IP.
1 parent 45cb33e commit 953f36f

File tree

7 files changed

+161
-32
lines changed

7 files changed

+161
-32
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ env:
77
- GO111MODULE=on
88

99
before_script:
10-
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.15.0
10+
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.17.1
1111

1212
script:
1313
- golangci-lint run ./...

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contribu
4141
* [Yutaka Takeda](https://github.com/enobufs)
4242
* [Atsushi Watanabe](https://github.com/at-wat)
4343
* [Robert Eperjesi](https://github.com/epes)
44+
* [Sebastian Waisbrot](https://github.com/seppo0010)
4445

4546
### License
4647
MIT License - see [LICENSE](LICENSE) for full text

agent.go

Lines changed: 62 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ type Agent struct {
8585

8686
trickle bool
8787
tieBreaker uint64
88+
lite bool
8889
connectionState ConnectionState
8990
gatheringState GatheringState
9091

@@ -218,6 +219,9 @@ type AgentConfig struct {
218219
// or mark the connection as failed if no valid candidate is available
219220
CandidateSelectionTimeout *time.Duration
220221

222+
// Lite agents do not perform connectivity check and only provide host candidates.
223+
Lite bool
224+
221225
// HostAcceptanceMinWait specify a minimum wait time before selecting host candidates
222226
HostAcceptanceMinWait *time.Duration
223227
// HostAcceptanceMinWait specify a minimum wait time before selecting srflx candidates
@@ -232,6 +236,49 @@ type AgentConfig struct {
232236
Net *vnet.Net
233237
}
234238

239+
func containsCandidateType(candidateType CandidateType, candidateTypeList []CandidateType) bool {
240+
if candidateTypeList == nil {
241+
return false
242+
}
243+
for _, ct := range candidateTypeList {
244+
if ct == candidateType {
245+
return true
246+
}
247+
}
248+
return false
249+
}
250+
251+
func createMulticastDNS(mDNSMode MulticastDNSMode, mDNSName string, log logging.LeveledLogger) (*mdns.Conn, MulticastDNSMode, error) {
252+
if mDNSMode == MulticastDNSModeDisabled {
253+
return nil, mDNSMode, nil
254+
}
255+
256+
addr, mdnsErr := net.ResolveUDPAddr("udp4", mdns.DefaultAddress)
257+
if mdnsErr != nil {
258+
return nil, mDNSMode, mdnsErr
259+
}
260+
261+
l, mdnsErr := net.ListenUDP("udp4", addr)
262+
if mdnsErr != nil {
263+
// If ICE fails to start MulticastDNS server just warn the user and continue
264+
log.Errorf("Failed to enable mDNS, continuing in mDNS disabled mode: (%s)", mdnsErr)
265+
return nil, MulticastDNSModeDisabled, nil
266+
}
267+
268+
switch mDNSMode {
269+
case MulticastDNSModeQueryOnly:
270+
conn, err := mdns.Server(ipv4.NewPacketConn(l), &mdns.Config{})
271+
return conn, mDNSMode, err
272+
case MulticastDNSModeQueryAndGather:
273+
conn, err := mdns.Server(ipv4.NewPacketConn(l), &mdns.Config{
274+
LocalNames: []string{mDNSName},
275+
})
276+
return conn, mDNSMode, err
277+
default:
278+
return nil, mDNSMode, nil
279+
}
280+
}
281+
235282
// NewAgent creates a new Agent
236283
func NewAgent(config *AgentConfig) (*Agent, error) {
237284
if config.PortMax < config.PortMin {
@@ -255,41 +302,14 @@ func NewAgent(config *AgentConfig) (*Agent, error) {
255302
log := loggerFactory.NewLogger("ice")
256303

257304
var mDNSConn *mdns.Conn
258-
mDNSConn, err = func() (*mdns.Conn, error) {
259-
if mDNSMode == MulticastDNSModeDisabled {
260-
return nil, nil
261-
}
262-
263-
addr, mdnsErr := net.ResolveUDPAddr("udp4", mdns.DefaultAddress)
264-
if mdnsErr != nil {
265-
return nil, mdnsErr
266-
}
267-
268-
l, mdnsErr := net.ListenUDP("udp4", addr)
269-
if mdnsErr != nil {
270-
// If ICE fails to start MulticastDNS server just warn the user and continue
271-
log.Errorf("Failed to enable mDNS, continuing in mDNS disabled mode: (%s)", mdnsErr)
272-
mDNSMode = MulticastDNSModeDisabled
273-
return nil, nil
274-
}
275-
276-
switch mDNSMode {
277-
case MulticastDNSModeQueryOnly:
278-
return mdns.Server(ipv4.NewPacketConn(l), &mdns.Config{})
279-
case MulticastDNSModeQueryAndGather:
280-
return mdns.Server(ipv4.NewPacketConn(l), &mdns.Config{
281-
LocalNames: []string{mDNSName},
282-
})
283-
default:
284-
return nil, nil
285-
}
286-
}()
305+
mDNSConn, mDNSMode, err = createMulticastDNS(mDNSMode, mDNSName, log)
287306
if err != nil {
288307
return nil, err
289308
}
290309

291310
a := &Agent{
292311
tieBreaker: rand.New(rand.NewSource(time.Now().UnixNano())).Uint64(),
312+
lite: config.Lite,
293313
gatheringState: GatheringStateNew,
294314
connectionState: ConnectionStateNew,
295315
localCandidates: make(map[NetworkType][]Candidate),
@@ -395,6 +415,14 @@ func NewAgent(config *AgentConfig) (*Agent, error) {
395415
a.candidateTypes = config.CandidateTypes
396416
}
397417

418+
if a.lite && (len(a.candidateTypes) != 1 || a.candidateTypes[0] != CandidateTypeHost) {
419+
return nil, ErrLiteUsingNonHostCandidates
420+
}
421+
422+
if config.Urls != nil && len(config.Urls) > 0 && !containsCandidateType(CandidateTypeServerReflexive, a.candidateTypes) && !containsCandidateType(CandidateTypeRelay, a.candidateTypes) {
423+
return nil, ErrUselessUrlsProvided
424+
}
425+
398426
go a.taskLoop()
399427

400428
// Initialize local candidates
@@ -459,6 +487,10 @@ func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remoteP
459487
a.selector = &controlledSelector{agent: a, log: a.log}
460488
}
461489

490+
if a.lite {
491+
a.selector = &liteSelector{pairCandidateSelector: a.selector}
492+
}
493+
462494
a.selector.Start()
463495

464496
agent.updateConnectionState(ConnectionStateChecking)
@@ -967,7 +999,7 @@ func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr)
967999
}
9681000
remoteCandidate = prflxCandidate
9691001

970-
a.log.Debugf("adding a new peer-reflexive candiate: %s ", remote)
1002+
a.log.Debugf("adding a new peer-reflexive candidate: %s ", remote)
9711003
a.addRemoteCandidate(remoteCandidate)
9721004
}
9731005

agent_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,81 @@ func TestConnectivityOnStartup(t *testing.T) {
432432
}
433433
}
434434

435+
func TestConnectivityLite(t *testing.T) {
436+
stunServerURL := &URL{
437+
Scheme: SchemeTypeSTUN,
438+
Host: "1.2.3.4",
439+
Port: 3478,
440+
Proto: ProtoTypeUDP,
441+
}
442+
443+
v, err := buildVNet(&vnet.NATType{
444+
MappingBehavior: vnet.EndpointIndependent,
445+
FilteringBehavior: vnet.EndpointIndependent,
446+
})
447+
require.NoError(t, err, "should succeed")
448+
defer v.close()
449+
450+
aNotifier, aConnected := onConnected()
451+
bNotifier, bConnected := onConnected()
452+
453+
var wg sync.WaitGroup
454+
wg.Add(2)
455+
456+
cfg0 := &AgentConfig{
457+
Urls: []*URL{stunServerURL},
458+
Trickle: true,
459+
NetworkTypes: supportedNetworkTypes,
460+
MulticastDNSMode: MulticastDNSModeDisabled,
461+
Net: v.net0,
462+
463+
taskLoopInterval: time.Hour,
464+
}
465+
466+
aAgent, err := NewAgent(cfg0)
467+
require.NoError(t, err)
468+
require.NoError(t, aAgent.OnConnectionStateChange(aNotifier))
469+
require.NoError(t, aAgent.OnCandidate(func(candidate Candidate) {
470+
if candidate == nil {
471+
wg.Done()
472+
}
473+
}))
474+
require.NoError(t, aAgent.GatherCandidates())
475+
476+
cfg1 := &AgentConfig{
477+
Urls: []*URL{},
478+
Trickle: true,
479+
Lite: true,
480+
CandidateTypes: []CandidateType{CandidateTypeHost},
481+
NetworkTypes: supportedNetworkTypes,
482+
MulticastDNSMode: MulticastDNSModeDisabled,
483+
Net: v.net1,
484+
taskLoopInterval: time.Hour,
485+
}
486+
487+
bAgent, err := NewAgent(cfg1)
488+
require.NoError(t, err)
489+
require.NoError(t, bAgent.OnConnectionStateChange(bNotifier))
490+
require.NoError(t, bAgent.OnCandidate(func(candidate Candidate) {
491+
if candidate == nil {
492+
wg.Done()
493+
}
494+
}))
495+
require.NoError(t, bAgent.GatherCandidates())
496+
497+
wg.Wait()
498+
aConn, bConn := connectWithVNet(aAgent, bAgent)
499+
500+
// Ensure pair selected
501+
// Note: this assumes ConnectionStateConnected is thrown after selecting the final pair
502+
<-aConnected
503+
<-bConnected
504+
505+
if !closePipe(t, aConn, bConn) {
506+
return
507+
}
508+
}
509+
435510
func TestInboundValidity(t *testing.T) {
436511
buildMsg := func(class stun.MessageClass, username, key string) *stun.Message {
437512
msg, err := stun.Build(stun.NewType(stun.MethodBinding, class), stun.TransactionID,

candidatepair_state.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func (c CandidatePairState) String() string {
3131
case CandidatePairStateFailed:
3232
return "failed"
3333
case CandidatePairStateSucceeded:
34-
return "succeded"
34+
return "succeeded"
3535
}
3636
return "Unknown candidate pair state"
3737
}

errors.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,11 @@ var (
5757

5858
// ErrAddressParseFailed indicates we were unable to parse a candidate address
5959
ErrAddressParseFailed = errors.New("failed to parse address")
60+
61+
// ErrLiteUsingNonHostCandidates indicates non host candidates were selected for a lite agent
62+
ErrLiteUsingNonHostCandidates = errors.New("lite agents must only use host candidates")
63+
64+
// ErrUselessUrlsProvided indicates that one or more URL was provided to the agent but no host
65+
// candidate required them
66+
ErrUselessUrlsProvided = errors.New("agent does not need URL with selected candidate types")
6067
)

selection.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,3 +298,17 @@ func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote
298298
s.PingCandidate(local, remote)
299299
}
300300
}
301+
302+
type liteSelector struct {
303+
pairCandidateSelector
304+
}
305+
306+
// A lite selector should not contact candidates
307+
func (s *liteSelector) ContactCandidates() {
308+
if _, ok := s.pairCandidateSelector.(*controllingSelector); ok {
309+
// pion/ice#96
310+
// TODO: implement lite controlling agent. For now falling back to full agent.
311+
// This only happens if both peers are lite. See RFC 8445 S6.1.1 and S6.2
312+
s.pairCandidateSelector.ContactCandidates()
313+
}
314+
}

0 commit comments

Comments
 (0)