Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/sip-fix-trunk-encryption.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@livekit/protocol": patch
"github.com/livekit/protocol": patch
---

Fix SIP trunk-level MediaEncryption being silently dropped on outbound and inbound calls. The early `req.Upgrade()` / `rule.Upgrade()` calls pinned `Media.Encryption` to the (legacy) request/rule field before the trunk's MediaEncryption was merged, causing INVITEs to omit SRTP when only the trunk had it configured.
3 changes: 2 additions & 1 deletion livekit/sip.go
Original file line number Diff line number Diff line change
Expand Up @@ -1081,7 +1081,8 @@ func (p *SIPMediaConfig) UpgradeWith(enc SIPMediaEncryption) *SIPMediaConfig {
if p == nil {
p = new(SIPMediaConfig)
}
if p.Encryption == nil {
// Ignore DISABLE as it's a zero value which means "unset".
if p.Encryption == nil && enc != SIPMediaEncryption_SIP_MEDIA_ENCRYPT_DISABLE {
p.Encryption = &enc
}
return p
Expand Down
25 changes: 25 additions & 0 deletions rpc/sip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,28 @@ func TestNewCreateSIPParticipantRequest(t *testing.T) {
require.NoError(t, err)
require.True(t, proto.Equal(exp, res), "%v\nvs\n%v", exp, res)
}

// Regression: trunk-level MediaEncryption must be honored when the request specifies
// neither MediaEncryption nor Media. A prior version called req.Upgrade() at the top of
// NewCreateSIPParticipantRequest, which pinned req.Media.Encryption to req.MediaEncryption (0)
// before the trunk was consulted, causing outbound INVITEs to omit SRTP and upstream
// providers (e.g. Twilio) to reject with 488 / 32208.
func TestNewCreateSIPParticipantRequest_TrunkOnlyEncryption(t *testing.T) {
r := &livekit.CreateSIPParticipantRequest{
SipTrunkId: "trunk",
SipCallTo: "+3333",
RoomName: "room",
}
tr := &livekit.SIPOutboundTrunkInfo{
SipTrunkId: "trunk",
Address: "sip.example.com",
Numbers: []string{"+1111"},
MediaEncryption: livekit.SIPMediaEncryption_SIP_MEDIA_ENCRYPT_REQUIRE,
}
res, err := NewCreateSIPParticipantRequest("p_123", "call-id", "xyz.sip.livekit.cloud", "url", "token", r, tr)
require.NoError(t, err)
require.Equal(t, livekit.SIPMediaEncryption_SIP_MEDIA_ENCRYPT_REQUIRE, res.MediaEncryption)
require.NotNil(t, res.Media)
require.NotNil(t, res.Media.Encryption)
require.Equal(t, livekit.SIPMediaEncryption_SIP_MEDIA_ENCRYPT_REQUIRE, *res.Media.Encryption)
}
28 changes: 28 additions & 0 deletions sip/sip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,34 @@ func TestEvaluateDispatchRule(t *testing.T) {
})
}

// Regression: trunk-level MediaEncryption must be honored when the dispatch rule specifies
// neither MediaEncryption nor Media. A prior version called rule.Upgrade() at the top of
// EvaluateDispatchRule, which pinned rule.Media.Encryption to rule.MediaEncryption (0)
// before the trunk was consulted, causing the inbound trunk's encryption setting to be
// silently dropped.
func TestEvaluateDispatchRule_TrunkOnlyEncryption(t *testing.T) {
d := &livekit.SIPDispatchRuleInfo{
SipDispatchRuleId: "rule",
Rule: newDirectDispatch("room", ""),
}
r := &rpc.EvaluateSIPDispatchRulesRequest{
SipCallId: "call-id",
CallingNumber: "+11112222",
CallingHost: "sip.example.com",
CalledNumber: "+3333",
}
tr := &livekit.SIPInboundTrunkInfo{
SipTrunkId: "trunk",
MediaEncryption: livekit.SIPMediaEncryption_SIP_MEDIA_ENCRYPT_REQUIRE,
}
res, err := EvaluateDispatchRule("p_123", tr, d, r)
require.NoError(t, err)
require.Equal(t, livekit.SIPMediaEncryption_SIP_MEDIA_ENCRYPT_REQUIRE, res.MediaEncryption)
require.NotNil(t, res.Media)
require.NotNil(t, res.Media.Encryption)
require.Equal(t, livekit.SIPMediaEncryption_SIP_MEDIA_ENCRYPT_REQUIRE, *res.Media.Encryption)
}

func TestMatchIP(t *testing.T) {
cases := []struct {
addr string
Expand Down
Loading