mirror of
https://github.com/pion/ice.git
synced 2025-10-24 07:53:12 +08:00
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.
This commit is contained in:
committed by
Sean DuBois
parent
45cb33ebe6
commit
953f36f07f
@@ -7,7 +7,7 @@ env:
|
||||
- GO111MODULE=on
|
||||
|
||||
before_script:
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.15.0
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.17.1
|
||||
|
||||
script:
|
||||
- golangci-lint run ./...
|
||||
|
||||
@@ -41,6 +41,7 @@ Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contribu
|
||||
* [Yutaka Takeda](https://github.com/enobufs)
|
||||
* [Atsushi Watanabe](https://github.com/at-wat)
|
||||
* [Robert Eperjesi](https://github.com/epes)
|
||||
* [Sebastian Waisbrot](https://github.com/seppo0010)
|
||||
|
||||
### License
|
||||
MIT License - see [LICENSE](LICENSE) for full text
|
||||
|
||||
92
agent.go
92
agent.go
@@ -85,6 +85,7 @@ type Agent struct {
|
||||
|
||||
trickle bool
|
||||
tieBreaker uint64
|
||||
lite bool
|
||||
connectionState ConnectionState
|
||||
gatheringState GatheringState
|
||||
|
||||
@@ -218,6 +219,9 @@ type AgentConfig struct {
|
||||
// or mark the connection as failed if no valid candidate is available
|
||||
CandidateSelectionTimeout *time.Duration
|
||||
|
||||
// Lite agents do not perform connectivity check and only provide host candidates.
|
||||
Lite bool
|
||||
|
||||
// HostAcceptanceMinWait specify a minimum wait time before selecting host candidates
|
||||
HostAcceptanceMinWait *time.Duration
|
||||
// HostAcceptanceMinWait specify a minimum wait time before selecting srflx candidates
|
||||
@@ -232,6 +236,49 @@ type AgentConfig struct {
|
||||
Net *vnet.Net
|
||||
}
|
||||
|
||||
func containsCandidateType(candidateType CandidateType, candidateTypeList []CandidateType) bool {
|
||||
if candidateTypeList == nil {
|
||||
return false
|
||||
}
|
||||
for _, ct := range candidateTypeList {
|
||||
if ct == candidateType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func createMulticastDNS(mDNSMode MulticastDNSMode, mDNSName string, log logging.LeveledLogger) (*mdns.Conn, MulticastDNSMode, error) {
|
||||
if mDNSMode == MulticastDNSModeDisabled {
|
||||
return nil, mDNSMode, nil
|
||||
}
|
||||
|
||||
addr, mdnsErr := net.ResolveUDPAddr("udp4", mdns.DefaultAddress)
|
||||
if mdnsErr != nil {
|
||||
return nil, mDNSMode, mdnsErr
|
||||
}
|
||||
|
||||
l, mdnsErr := net.ListenUDP("udp4", addr)
|
||||
if mdnsErr != 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)
|
||||
return nil, MulticastDNSModeDisabled, nil
|
||||
}
|
||||
|
||||
switch mDNSMode {
|
||||
case MulticastDNSModeQueryOnly:
|
||||
conn, err := mdns.Server(ipv4.NewPacketConn(l), &mdns.Config{})
|
||||
return conn, mDNSMode, err
|
||||
case MulticastDNSModeQueryAndGather:
|
||||
conn, err := mdns.Server(ipv4.NewPacketConn(l), &mdns.Config{
|
||||
LocalNames: []string{mDNSName},
|
||||
})
|
||||
return conn, mDNSMode, err
|
||||
default:
|
||||
return nil, mDNSMode, nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewAgent creates a new Agent
|
||||
func NewAgent(config *AgentConfig) (*Agent, error) {
|
||||
if config.PortMax < config.PortMin {
|
||||
@@ -255,41 +302,14 @@ func NewAgent(config *AgentConfig) (*Agent, error) {
|
||||
log := loggerFactory.NewLogger("ice")
|
||||
|
||||
var mDNSConn *mdns.Conn
|
||||
mDNSConn, err = func() (*mdns.Conn, error) {
|
||||
if mDNSMode == MulticastDNSModeDisabled {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
addr, mdnsErr := net.ResolveUDPAddr("udp4", mdns.DefaultAddress)
|
||||
if mdnsErr != nil {
|
||||
return nil, mdnsErr
|
||||
}
|
||||
|
||||
l, mdnsErr := net.ListenUDP("udp4", addr)
|
||||
if mdnsErr != 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)
|
||||
mDNSMode = MulticastDNSModeDisabled
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch mDNSMode {
|
||||
case MulticastDNSModeQueryOnly:
|
||||
return mdns.Server(ipv4.NewPacketConn(l), &mdns.Config{})
|
||||
case MulticastDNSModeQueryAndGather:
|
||||
return mdns.Server(ipv4.NewPacketConn(l), &mdns.Config{
|
||||
LocalNames: []string{mDNSName},
|
||||
})
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}()
|
||||
mDNSConn, mDNSMode, err = createMulticastDNS(mDNSMode, mDNSName, log)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a := &Agent{
|
||||
tieBreaker: rand.New(rand.NewSource(time.Now().UnixNano())).Uint64(),
|
||||
lite: config.Lite,
|
||||
gatheringState: GatheringStateNew,
|
||||
connectionState: ConnectionStateNew,
|
||||
localCandidates: make(map[NetworkType][]Candidate),
|
||||
@@ -395,6 +415,14 @@ func NewAgent(config *AgentConfig) (*Agent, error) {
|
||||
a.candidateTypes = config.CandidateTypes
|
||||
}
|
||||
|
||||
if a.lite && (len(a.candidateTypes) != 1 || a.candidateTypes[0] != CandidateTypeHost) {
|
||||
return nil, ErrLiteUsingNonHostCandidates
|
||||
}
|
||||
|
||||
if config.Urls != nil && len(config.Urls) > 0 && !containsCandidateType(CandidateTypeServerReflexive, a.candidateTypes) && !containsCandidateType(CandidateTypeRelay, a.candidateTypes) {
|
||||
return nil, ErrUselessUrlsProvided
|
||||
}
|
||||
|
||||
go a.taskLoop()
|
||||
|
||||
// Initialize local candidates
|
||||
@@ -459,6 +487,10 @@ func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remoteP
|
||||
a.selector = &controlledSelector{agent: a, log: a.log}
|
||||
}
|
||||
|
||||
if a.lite {
|
||||
a.selector = &liteSelector{pairCandidateSelector: a.selector}
|
||||
}
|
||||
|
||||
a.selector.Start()
|
||||
|
||||
agent.updateConnectionState(ConnectionStateChecking)
|
||||
@@ -967,7 +999,7 @@ func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr)
|
||||
}
|
||||
remoteCandidate = prflxCandidate
|
||||
|
||||
a.log.Debugf("adding a new peer-reflexive candiate: %s ", remote)
|
||||
a.log.Debugf("adding a new peer-reflexive candidate: %s ", remote)
|
||||
a.addRemoteCandidate(remoteCandidate)
|
||||
}
|
||||
|
||||
|
||||
@@ -432,6 +432,81 @@ func TestConnectivityOnStartup(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectivityLite(t *testing.T) {
|
||||
stunServerURL := &URL{
|
||||
Scheme: SchemeTypeSTUN,
|
||||
Host: "1.2.3.4",
|
||||
Port: 3478,
|
||||
Proto: ProtoTypeUDP,
|
||||
}
|
||||
|
||||
v, err := buildVNet(&vnet.NATType{
|
||||
MappingBehavior: vnet.EndpointIndependent,
|
||||
FilteringBehavior: vnet.EndpointIndependent,
|
||||
})
|
||||
require.NoError(t, err, "should succeed")
|
||||
defer v.close()
|
||||
|
||||
aNotifier, aConnected := onConnected()
|
||||
bNotifier, bConnected := onConnected()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
|
||||
cfg0 := &AgentConfig{
|
||||
Urls: []*URL{stunServerURL},
|
||||
Trickle: true,
|
||||
NetworkTypes: supportedNetworkTypes,
|
||||
MulticastDNSMode: MulticastDNSModeDisabled,
|
||||
Net: v.net0,
|
||||
|
||||
taskLoopInterval: time.Hour,
|
||||
}
|
||||
|
||||
aAgent, err := NewAgent(cfg0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, aAgent.OnConnectionStateChange(aNotifier))
|
||||
require.NoError(t, aAgent.OnCandidate(func(candidate Candidate) {
|
||||
if candidate == nil {
|
||||
wg.Done()
|
||||
}
|
||||
}))
|
||||
require.NoError(t, aAgent.GatherCandidates())
|
||||
|
||||
cfg1 := &AgentConfig{
|
||||
Urls: []*URL{},
|
||||
Trickle: true,
|
||||
Lite: true,
|
||||
CandidateTypes: []CandidateType{CandidateTypeHost},
|
||||
NetworkTypes: supportedNetworkTypes,
|
||||
MulticastDNSMode: MulticastDNSModeDisabled,
|
||||
Net: v.net1,
|
||||
taskLoopInterval: time.Hour,
|
||||
}
|
||||
|
||||
bAgent, err := NewAgent(cfg1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, bAgent.OnConnectionStateChange(bNotifier))
|
||||
require.NoError(t, bAgent.OnCandidate(func(candidate Candidate) {
|
||||
if candidate == nil {
|
||||
wg.Done()
|
||||
}
|
||||
}))
|
||||
require.NoError(t, bAgent.GatherCandidates())
|
||||
|
||||
wg.Wait()
|
||||
aConn, bConn := connectWithVNet(aAgent, bAgent)
|
||||
|
||||
// Ensure pair selected
|
||||
// Note: this assumes ConnectionStateConnected is thrown after selecting the final pair
|
||||
<-aConnected
|
||||
<-bConnected
|
||||
|
||||
if !closePipe(t, aConn, bConn) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestInboundValidity(t *testing.T) {
|
||||
buildMsg := func(class stun.MessageClass, username, key string) *stun.Message {
|
||||
msg, err := stun.Build(stun.NewType(stun.MethodBinding, class), stun.TransactionID,
|
||||
|
||||
@@ -31,7 +31,7 @@ func (c CandidatePairState) String() string {
|
||||
case CandidatePairStateFailed:
|
||||
return "failed"
|
||||
case CandidatePairStateSucceeded:
|
||||
return "succeded"
|
||||
return "succeeded"
|
||||
}
|
||||
return "Unknown candidate pair state"
|
||||
}
|
||||
|
||||
@@ -57,4 +57,11 @@ var (
|
||||
|
||||
// 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 = 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
|
||||
ErrUselessUrlsProvided = errors.New("agent does not need URL with selected candidate types")
|
||||
)
|
||||
|
||||
14
selection.go
14
selection.go
@@ -298,3 +298,17 @@ func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote
|
||||
s.PingCandidate(local, remote)
|
||||
}
|
||||
}
|
||||
|
||||
type liteSelector struct {
|
||||
pairCandidateSelector
|
||||
}
|
||||
|
||||
// A lite selector should not contact candidates
|
||||
func (s *liteSelector) ContactCandidates() {
|
||||
if _, ok := s.pairCandidateSelector.(*controllingSelector); ok {
|
||||
// pion/ice#96
|
||||
// TODO: implement lite controlling agent. For now falling back to full agent.
|
||||
// This only happens if both peers are lite. See RFC 8445 S6.1.1 and S6.2
|
||||
s.pairCandidateSelector.ContactCandidates()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user