@@ -42,6 +42,7 @@ import (
4242 "github.com/livekit/protocol/rpc"
4343 lksip "github.com/livekit/protocol/sip"
4444 "github.com/livekit/protocol/tracer"
45+ "github.com/livekit/protocol/utils"
4546 "github.com/livekit/protocol/utils/traceid"
4647 "github.com/livekit/psrpc"
4748 lksdk "github.com/livekit/server-sdk-go/v2"
@@ -64,6 +65,8 @@ const (
6465 inviteOKRetryAttempts = 5
6566 inviteOKRetryAttemptsNoACK = 2
6667 inviteOkAckLateTimeout = inviteOkRetryIntervalMax
68+
69+ inviteCredentialValidity = 60 * time .Minute // Allow reuse of credentials for 1h
6770)
6871
6972var errNoACK = errors .New ("no ACK received for 200 OK" )
@@ -134,23 +137,50 @@ func (s *Server) getCallInfo(id string) *inboundCallInfo {
134137 return c
135138}
136139
137- func (s * Server ) getInvite (sipCallID string ) * inProgressInvite {
138- s .imu .Lock ()
139- defer s .imu .Unlock ()
140- for i := range s .inProgressInvites {
141- if s .inProgressInvites [i ].sipCallID == sipCallID {
142- return s .inProgressInvites [i ]
140+ func (s * Server ) cleanupInvites () {
141+ ticker := time .NewTicker (5 * time .Minute ) // Periodic cleanup every 5 minutes
142+ defer ticker .Stop ()
143+ for {
144+ select {
145+ case <- s .closing .Watch ():
146+ return
147+ case <- ticker .C :
148+ s .imu .Lock ()
149+ for it := s .inviteTimeoutQueue .IterateRemoveAfter (inviteCredentialValidity ); it .Next (); {
150+ key := it .Item ().Value
151+ delete (s .inProgressInvites , key )
152+ }
153+ s .imu .Unlock ()
143154 }
144155 }
145- if len (s .inProgressInvites ) >= digestLimit {
146- s .inProgressInvites = s .inProgressInvites [1 :]
156+ }
157+
158+ func (s * Server ) getInvite (sipCallID , toTag , fromTag string ) * inProgressInvite {
159+ key := dialogKey {
160+ sipCallID : sipCallID ,
161+ toTag : toTag ,
162+ fromTag : fromTag ,
163+ }
164+
165+ s .imu .RLock ()
166+ is , exists := s .inProgressInvites [key ]
167+ s .imu .RUnlock ()
168+ if ! exists {
169+ s .imu .Lock ()
170+ is , exists = s .inProgressInvites [key ]
171+ if ! exists {
172+ is = & inProgressInvite {sipCallID : sipCallID , timeoutLink : utils.TimeoutQueueItem [dialogKey ]{Value : key }}
173+ s .inProgressInvites [key ] = is
174+ }
175+ s .imu .Unlock ()
147176 }
148- is := & inProgressInvite {sipCallID : sipCallID }
149- s .inProgressInvites = append (s .inProgressInvites , is )
177+
178+ // Always reset the timeout link, whether just created or not
179+ s .inviteTimeoutQueue .Reset (& is .timeoutLink )
150180 return is
151181}
152182
153- func (s * Server ) handleInviteAuth (tid traceid.ID , log logger.Logger , req * sip.Request , tx sip.ServerTransaction , from , username , password string ) (ok bool ) {
183+ func (s * Server ) handleInviteAuth (tid traceid.ID , log logger.Logger , req * sip.Request , tx sip.ServerTransaction , from , username , password string , inviteState * inProgressInvite ) (ok bool ) {
154184 log = log .WithValues (
155185 "username" , username ,
156186 "passwordHash" , hashPassword (password ),
@@ -171,14 +201,6 @@ func (s *Server) handleInviteAuth(tid traceid.ID, log logger.Logger, req *sip.Re
171201 _ = tx .Respond (sip .NewResponseFromRequest (req , 100 , "Processing" , nil ))
172202 }
173203
174- // Extract SIP Call ID for tracking in-progress invites
175- sipCallID := ""
176- if h := req .CallID (); h != nil {
177- sipCallID = h .Value ()
178- }
179- inviteState := s .getInvite (sipCallID )
180- log = log .WithValues ("inviteStateSipCallID" , sipCallID )
181-
182204 h := req .GetHeader ("Proxy-Authorization" )
183205 if h == nil {
184206 inviteState .challenge = digest.Challenge {
@@ -230,7 +252,6 @@ func (s *Server) handleInviteAuth(tid traceid.ID, log logger.Logger, req *sip.Re
230252 // Check if we have a valid challenge state
231253 if inviteState .challenge .Realm == "" {
232254 log .Warnw ("No challenge state found for authentication attempt" , errors .New ("missing challenge state" ),
233- "sipCallID" , sipCallID ,
234255 "expectedRealm" , UserAgent ,
235256 )
236257 _ = tx .Respond (sip .NewResponseFromRequest (req , 401 , "Bad credentials" , nil ))
@@ -305,20 +326,18 @@ func (s *Server) processInvite(req *sip.Request, tx sip.ServerTransaction) (retE
305326 s .log .Errorw ("cannot parse source IP" , err , "fromIP" , src )
306327 return psrpc .NewError (psrpc .MalformedRequest , errors .Wrap (err , "cannot parse source IP" ))
307328 }
308- callID := lksip .NewCallID ()
309- tid := traceid .FromGUID (callID )
329+ sipCallID := legCallIDFromReq (req )
310330 tr := callTransportFromReq (req )
311331 legTr := legTransportFromReq (req )
312332 log := s .log .WithValues (
313- "callID" , callID ,
314- "traceID" , tid .String (),
333+ "sipCallID" , sipCallID ,
315334 "fromIP" , src .Addr (),
316335 "toIP" , req .Destination (),
317336 "transport" , tr ,
318337 )
319338
320339 var call * inboundCall
321- cc := s .newInbound (log , LocalTag ( callID ), s .ContactURI (legTr ), req , tx , func (headers map [string ]string ) map [string ]string {
340+ cc := s .newInbound (log , s .ContactURI (legTr ), req , tx , func (headers map [string ]string ) map [string ]string {
322341 c := call
323342 if c == nil || len (c .attrsToHdr ) == 0 {
324343 return headers
@@ -331,25 +350,53 @@ func (s *Server) processInvite(req *sip.Request, tx sip.ServerTransaction) (retE
331350 })
332351 log = LoggerWithParams (log , cc )
333352 log = LoggerWithHeaders (log , cc )
334- cc .log = log
335- log .Infow ("processing invite" )
336353
337354 if err := cc .ValidateInvite (); err != nil {
355+ log .Errorw ("invalid invite" , err )
338356 if s .conf .HideInboundPort {
339357 cc .Drop ()
340358 } else {
341359 cc .RespondAndDrop (sip .StatusBadRequest , "Bad request" )
342360 }
343361 return psrpc .NewError (psrpc .InvalidArgument , errors .Wrap (err , "invite validation failed" ))
344362 }
363+
364+ // Establish ID
365+ fromTag , _ := req .From ().Params .Get ("tag" ) // always exists, via ValidateInvite() check
366+ toParams := req .To ().Params // To() always exists, via ValidateInvite() check
367+ if toParams == nil {
368+ toParams = sip .NewParams ()
369+ req .To ().Params = toParams
370+ }
371+ toTag , ok := toParams .Get ("tag" )
372+ if ! ok {
373+ // No to-tag on the invite means we need to generate one per RFC 3261 section 12.
374+ // Generate a new to-tag early, to make sure both INVITES have the same ID.
375+ toTag = utils .NewGuid ("" )
376+ toParams .Add ("tag" , toTag )
377+ }
378+ inviteProgress := s .getInvite (sipCallID , toTag , fromTag )
379+ callID := inviteProgress .lkCallID
380+ if callID == "" {
381+ callID = lksip .NewCallID ()
382+ inviteProgress .lkCallID = callID
383+ }
384+ cc .id = LocalTag (callID )
385+ tid := traceid .FromGUID (sipCallID )
386+
387+ log = log .WithValues ("callID" , callID )
388+ log = log .WithValues ("traceID" , tid .String ())
389+ cc .log = log
390+ log .Infow ("processing invite" )
391+
345392 ctx , span := tracer .Start (ctx , "Server.onInvite" )
346393 defer span .End ()
347394
348395 s .cmu .RLock ()
349- existing := s .byCallID [cc . SIPCallID () ]
396+ existing := s .byCallID [sipCallID ]
350397 s .cmu .RUnlock ()
351398 if existing != nil && existing .cc .InviteCSeq () < cc .InviteCSeq () {
352- log .Infow ("accepting reinvite" , "sipCallID" , existing . cc . ID (), " content-type" , req .ContentType (), "content-length" , req .ContentLength ())
399+ log .Infow ("accepting reinvite" , "content-type" , req .ContentType (), "content-length" , req .ContentLength ())
353400 existing .log ().Infow ("reinvite" , "content-type" , req .ContentType (), "content-length" , req .ContentLength (), "cseq" , cc .InviteCSeq ())
354401 cc .AcceptAsKeepAlive (existing .cc .OwnSDP ())
355402 return nil
@@ -376,7 +423,7 @@ func (s *Server) processInvite(req *sip.Request, tx sip.ServerTransaction) (retE
376423
377424 callInfo := & rpc.SIPCall {
378425 LkCallId : callID ,
379- SipCallId : cc . SIPCallID () ,
426+ SipCallId : sipCallID ,
380427 SourceIp : src .Addr ().String (),
381428 Address : ToSIPUri ("" , cc .Address ()),
382429 From : ToSIPUri ("" , from ),
@@ -447,15 +494,15 @@ func (s *Server) processInvite(req *sip.Request, tx sip.ServerTransaction) (retE
447494 // We will send password request anyway, so might as well signal that the progress is made.
448495 cc .Processing ()
449496 }
450- s .getCallInfo (cc . SIPCallID () ).countInvite (log , req )
451- if ! s .handleInviteAuth (tid , log , req , tx , from .User , r .Username , r .Password ) {
497+ s .getCallInfo (sipCallID ).countInvite (log , req )
498+ if ! s .handleInviteAuth (tid , log , req , tx , from .User , r .Username , r .Password , inviteProgress ) {
452499 cmon .InviteErrorShort ("unauthorized" )
453500 // handleInviteAuth will generate the SIP Response as needed
454501 return psrpc .NewErrorf (psrpc .PermissionDenied , "invalid credentials were provided" )
455502 }
456503 // ok
457504 case AuthAccept :
458- s .getCallInfo (cc . SIPCallID () ).countInvite (log , req )
505+ s .getCallInfo (sipCallID ).countInvite (log , req )
459506 // ok
460507 }
461508
@@ -1374,11 +1421,10 @@ func (c *inboundCall) transferCall(ctx context.Context, transferTo string, heade
13741421
13751422}
13761423
1377- func (s * Server ) newInbound (log logger.Logger , id LocalTag , contact URI , invite * sip.Request , inviteTx sip.ServerTransaction , getHeaders setHeadersFunc ) * sipInbound {
1424+ func (s * Server ) newInbound (log logger.Logger , contact URI , invite * sip.Request , inviteTx sip.ServerTransaction , getHeaders setHeadersFunc ) * sipInbound {
13781425 c := & sipInbound {
13791426 log : log ,
13801427 s : s ,
1381- id : id ,
13821428 invite : invite ,
13831429 inviteTx : inviteTx ,
13841430 legTr : legTransportFromReq (invite ),
0 commit comments