Fix chicken-egg problem in the ice agent creation

This commit is contained in:
Zoltán Papp 2024-06-19 11:28:01 +02:00
parent 48310ef99c
commit 2f32e0d8cf
2 changed files with 58 additions and 18 deletions

View File

@ -393,7 +393,7 @@ func (conn *Conn) iCEConnectionIsReady(priority ConnPriority, iceConnInfo ICECon
} }
endpointUdpAddr, _ := net.ResolveUDPAddr(endpoint.Network(), endpoint.String()) endpointUdpAddr, _ := net.ResolveUDPAddr(endpoint.Network(), endpoint.String())
conn.log.Debugf("Conn resolved IP for %s: %s", endpoint, endpointUdpAddr.IP) conn.log.Debugf("Conn resolved IP is %s for endopint %s", endpoint, endpointUdpAddr.IP)
conn.connID = nbnet.GenerateConnID() conn.connID = nbnet.GenerateConnID()
for _, hook := range conn.beforeAddPeerHooks { for _, hook := range conn.beforeAddPeerHooks {
@ -461,20 +461,20 @@ func (conn *Conn) doHandshake() (*OfferAnswer, error) {
return nil, ErrSignalIsNotReady return nil, ErrSignalIsNotReady
} }
uFreg, pwd, err := conn.workerICE.GetLocalUserCredentials() var (
ha HandshakeArgs
err error
)
ha.IceUFrag, ha.IcePwd, err = conn.workerICE.GetLocalUserCredentials()
if err != nil { if err != nil {
conn.log.Errorf("failed to get local user credentials: %v", err) conn.log.Errorf("failed to get local user credentials: %v", err)
} }
addr, err := conn.workerRelay.RelayAddress() addr, err := conn.workerRelay.RelayAddress()
if err != nil { if err == nil {
conn.log.Errorf("failed to get local relay address: %v", err) ha.RelayAddr = addr.String()
} }
return conn.handshaker.Handshake(HandshakeArgs{ return conn.handshaker.Handshake(ha)
uFreg,
pwd,
addr.String(),
})
} }
func (conn *Conn) evalStatus() ConnStatus { func (conn *Conn) evalStatus() ConnStatus {

View File

@ -13,6 +13,7 @@ import (
"time" "time"
"github.com/pion/ice/v3" "github.com/pion/ice/v3"
"github.com/pion/randutil"
"github.com/pion/stun/v2" "github.com/pion/stun/v2"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -27,6 +28,14 @@ const (
iceDisconnectedTimeoutDefault = 6 * time.Second iceDisconnectedTimeoutDefault = 6 * time.Second
// iceRelayAcceptanceMinWaitDefault is the same as in the Pion ICE package // iceRelayAcceptanceMinWaitDefault is the same as in the Pion ICE package
iceRelayAcceptanceMinWaitDefault = 2 * time.Second iceRelayAcceptanceMinWaitDefault = 2 * time.Second
lenUFrag = 16
lenPwd = 32
runesAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
)
var (
failedTimeout = 6 * time.Second
) )
type ICEConfig struct { type ICEConfig struct {
@ -74,11 +83,13 @@ type WorkerICE struct {
selectedPriority ConnPriority selectedPriority ConnPriority
agent *ice.Agent agent *ice.Agent
muxAgent sync.RWMutex muxAgent sync.Mutex
StunTurn []*stun.URI StunTurn []*stun.URI
sentExtraSrflx bool sentExtraSrflx bool
localUfrag string
localPwd string
} }
func NewWorkerICE(ctx context.Context, log *log.Entry, config ConnConfig, configICE ICEConfig, signaler *Signaler, ifaceDiscover stdnet.ExternalIFaceDiscover, statusRecorder *Status, onICEConnReady OnICEConnReadyCallback, onStatusChanged func(ConnStatus), doHandshakeFn DoHandshake) *WorkerICE { func NewWorkerICE(ctx context.Context, log *log.Entry, config ConnConfig, configICE ICEConfig, signaler *Signaler, ifaceDiscover stdnet.ExternalIFaceDiscover, statusRecorder *Status, onICEConnReady OnICEConnReadyCallback, onStatusChanged func(ConnStatus), doHandshakeFn DoHandshake) *WorkerICE {
@ -127,13 +138,22 @@ func (w *WorkerICE) SetupICEConnection(hasRelayOnLocally bool) {
} }
ctx, ctxCancel := context.WithCancel(w.ctx) ctx, ctxCancel := context.WithCancel(w.ctx)
w.muxAgent.Lock()
agent, err := w.reCreateAgent(ctxCancel, preferredCandidateTypes) agent, err := w.reCreateAgent(ctxCancel, preferredCandidateTypes)
if err != nil { if err != nil {
ctxCancel() ctxCancel()
w.muxAgent.Unlock()
continue continue
} }
w.muxAgent.Lock()
w.agent = agent w.agent = agent
// generate credentials for the next loop. Important the credentials are generated before handshake, because
// the handshake could provide a cached offer-answer
w.localUfrag, w.localPwd, err = generateICECredentials()
if err != nil {
ctxCancel()
w.muxAgent.Unlock()
continue
}
w.muxAgent.Unlock() w.muxAgent.Unlock()
err = w.agent.GatherCandidates() err = w.agent.GatherCandidates()
@ -191,8 +211,8 @@ func (w *WorkerICE) SetupICEConnection(hasRelayOnLocally bool) {
// OnRemoteCandidate Handles ICE connection Candidate provided by the remote peer. // OnRemoteCandidate Handles ICE connection Candidate provided by the remote peer.
func (w *WorkerICE) OnRemoteCandidate(candidate ice.Candidate, haRoutes route.HAMap) { func (w *WorkerICE) OnRemoteCandidate(candidate ice.Candidate, haRoutes route.HAMap) {
w.muxAgent.RLocker() w.muxAgent.Lock()
defer w.muxAgent.RUnlock() defer w.muxAgent.Unlock()
w.log.Debugf("OnRemoteCandidate from peer %s -> %s", w.config.Key, candidate.String()) w.log.Debugf("OnRemoteCandidate from peer %s -> %s", w.config.Key, candidate.String())
if w.agent == nil { if w.agent == nil {
return return
@ -210,14 +230,18 @@ func (w *WorkerICE) OnRemoteCandidate(candidate ice.Candidate, haRoutes route.HA
} }
func (w *WorkerICE) GetLocalUserCredentials() (frag string, pwd string, err error) { func (w *WorkerICE) GetLocalUserCredentials() (frag string, pwd string, err error) {
if w.agent == nil { w.muxAgent.Lock()
return "", "", errors.New("ICE Agent is not initialized") defer w.muxAgent.Unlock()
if w.localUfrag != "" && w.localPwd != "" {
return w.localUfrag, w.localPwd, nil
} }
return w.agent.GetLocalUserCredentials()
w.localUfrag, w.localPwd, err = generateICECredentials()
return w.localUfrag, w.localPwd, err
} }
func (w *WorkerICE) reCreateAgent(ctxCancel context.CancelFunc, relaySupport []ice.CandidateType) (*ice.Agent, error) { func (w *WorkerICE) reCreateAgent(ctxCancel context.CancelFunc, relaySupport []ice.CandidateType) (*ice.Agent, error) {
failedTimeout := 6 * time.Second
transportNet, err := w.newStdNet() transportNet, err := w.newStdNet()
if err != nil { if err != nil {
w.log.Errorf("failed to create pion's stdnet: %s", err) w.log.Errorf("failed to create pion's stdnet: %s", err)
@ -232,15 +256,17 @@ func (w *WorkerICE) reCreateAgent(ctxCancel context.CancelFunc, relaySupport []i
NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4, ice.NetworkTypeUDP6}, NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4, ice.NetworkTypeUDP6},
Urls: w.configICE.StunTurn.Load().([]*stun.URI), Urls: w.configICE.StunTurn.Load().([]*stun.URI),
CandidateTypes: relaySupport, CandidateTypes: relaySupport,
FailedTimeout: &failedTimeout,
InterfaceFilter: stdnet.InterfaceFilter(w.configICE.InterfaceBlackList), InterfaceFilter: stdnet.InterfaceFilter(w.configICE.InterfaceBlackList),
UDPMux: w.configICE.UDPMux, UDPMux: w.configICE.UDPMux,
UDPMuxSrflx: w.configICE.UDPMuxSrflx, UDPMuxSrflx: w.configICE.UDPMuxSrflx,
NAT1To1IPs: w.configICE.NATExternalIPs, NAT1To1IPs: w.configICE.NATExternalIPs,
Net: transportNet, Net: transportNet,
FailedTimeout: &failedTimeout,
DisconnectedTimeout: &iceDisconnectedTimeout, DisconnectedTimeout: &iceDisconnectedTimeout,
KeepaliveInterval: &iceKeepAlive, KeepaliveInterval: &iceKeepAlive,
RelayAcceptanceMinWait: &iceRelayAcceptanceMinWait, RelayAcceptanceMinWait: &iceRelayAcceptanceMinWait,
LocalUfrag: w.localUfrag,
LocalPwd: w.localPwd,
} }
if w.configICE.DisableIPv6Discovery { if w.configICE.DisableIPv6Discovery {
@ -448,3 +474,17 @@ func isRelayed(pair *ice.CandidatePair) bool {
} }
return false return false
} }
func generateICECredentials() (string, string, error) {
ufrag, err := randutil.GenerateCryptoRandomString(lenUFrag, runesAlpha)
if err != nil {
return "", "", err
}
pwd, err := randutil.GenerateCryptoRandomString(lenPwd, runesAlpha)
if err != nil {
return "", "", err
}
return ufrag, pwd, nil
}