Skip to content

Commit 75ff0fc

Browse files
authored
Log invite retry attempts. (#464)
* Log invite retry attempts. * Account for reinvites.
1 parent e495020 commit 75ff0fc

File tree

4 files changed

+74
-0
lines changed

4 files changed

+74
-0
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ require (
3333
)
3434

3535
require (
36+
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
3637
github.com/nyaruka/phonenumbers v1.6.5 // indirect
3738
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
3839
go.opentelemetry.io/otel v1.37.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
9797
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
9898
github.com/gotranspile/g722 v0.0.0-20240123003956-384a1bb16a19 h1:vqA29ogkaaq2GxFQsMA8TTFUSGc1lGaZtnKbuiP840c=
9999
github.com/gotranspile/g722 v0.0.0-20240123003956-384a1bb16a19/go.mod h1:AcVi4yM6DRZscpQXsEWBPItD52Saqw0x7md4mmjzUi8=
100+
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
101+
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
100102
github.com/icholy/digest v1.1.0 h1:HfGg9Irj7i+IX1o1QAmPfIBNu/Q5A5Tu3n/MED9k9H4=
101103
github.com/icholy/digest v1.1.0/go.mod h1:QNrsSGQ5v7v9cReDI0+eyjsXGUoRSUZQHeQ5C4XLa0Y=
102104
github.com/jfreymuth/oggvorbis v1.0.5 h1:u+Ck+R0eLSRhgq8WTmffYnrVtSztJcYrl588DM4e3kQ=

pkg/sip/inbound.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,63 @@ func hashPassword(password string) string {
7676
return hex.EncodeToString(hash[:8]) // Use first 8 bytes for shorter hash
7777
}
7878

79+
type inboundCallInfo struct {
80+
sync.Mutex
81+
cseq uint32
82+
cseqAuth uint32
83+
invites uint32
84+
invitesAuth uint32
85+
}
86+
87+
func inviteHasAuth(r *sip.Request) bool {
88+
return r.GetHeader("Proxy-Authorization") != nil ||
89+
r.GetHeader("Authorization") != nil
90+
}
91+
92+
func (c *inboundCallInfo) countInvite(log logger.Logger, req *sip.Request) {
93+
hasAuth := inviteHasAuth(req)
94+
cseq := req.CSeq()
95+
if cseq == nil {
96+
return
97+
}
98+
c.Lock()
99+
defer c.Unlock()
100+
cseqPtr := &c.cseq
101+
countPtr := &c.invites
102+
name := "invite"
103+
if hasAuth {
104+
cseqPtr = &c.cseqAuth
105+
countPtr = &c.invitesAuth
106+
name = "invite with auth"
107+
}
108+
if *cseqPtr == 0 {
109+
*cseqPtr = cseq.SeqNo
110+
}
111+
if cseq.SeqNo > *cseqPtr {
112+
return // reinvite
113+
}
114+
*countPtr++
115+
if *countPtr > 1 {
116+
log.Warnw("remote appears to be retrying an "+name, nil, "invites", *countPtr, "cseq", *cseqPtr)
117+
}
118+
}
119+
120+
func (s *Server) getCallInfo(id string) *inboundCallInfo {
121+
c, _ := s.infos.byCallID.Get(id)
122+
if c != nil {
123+
return c
124+
}
125+
s.infos.Lock()
126+
defer s.infos.Unlock()
127+
c, _ = s.infos.byCallID.Get(id)
128+
if c != nil {
129+
return c
130+
}
131+
c = &inboundCallInfo{}
132+
s.infos.byCallID.Add(id, c)
133+
return c
134+
}
135+
79136
func (s *Server) getInvite(sipCallID string) *inProgressInvite {
80137
s.imu.Lock()
81138
defer s.imu.Unlock()
@@ -118,6 +175,8 @@ func (s *Server) handleInviteAuth(log logger.Logger, req *sip.Request, tx sip.Se
118175
if h := req.CallID(); h != nil {
119176
sipCallID = h.Value()
120177
}
178+
ci := s.getCallInfo(sipCallID)
179+
ci.countInvite(log, req)
121180
inviteState := s.getInvite(sipCallID)
122181
log = log.WithValues("inviteStateSipCallID", sipCallID)
123182

pkg/sip/server.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"time"
2828

2929
"github.com/frostbyte73/core"
30+
"github.com/hashicorp/golang-lru/v2/expirable"
3031
"github.com/icholy/digest"
3132
"golang.org/x/exp/maps"
3233

@@ -46,6 +47,11 @@ const (
4647
digestLimit = 500
4748
)
4849

50+
const (
51+
maxCallCache = 5000 // ~8 B per entry, ~40 KB
52+
callCacheTTL = time.Minute // we only need it for detecting retries from providers for now
53+
)
54+
4955
var (
5056
contentTypeHeaderSDP = sip.ContentTypeHeader("application/sdp")
5157
)
@@ -133,6 +139,11 @@ type Server struct {
133139
activeCalls map[RemoteTag]*inboundCall
134140
byLocal map[LocalTag]*inboundCall
135141

142+
infos struct {
143+
sync.Mutex
144+
byCallID *expirable.LRU[string, *inboundCallInfo]
145+
}
146+
136147
handler Handler
137148
conf *config.Config
138149
sconf *ServiceConfig
@@ -158,6 +169,7 @@ func NewServer(region string, conf *config.Config, log logger.Logger, mon *stats
158169
activeCalls: make(map[RemoteTag]*inboundCall),
159170
byLocal: make(map[LocalTag]*inboundCall),
160171
}
172+
s.infos.byCallID = expirable.NewLRU[string, *inboundCallInfo](maxCallCache, nil, callCacheTTL)
161173
s.initMediaRes()
162174
return s
163175
}

0 commit comments

Comments
 (0)