Differently handling the case of no trunks defined vs trunks defined but none matching the call

This commit is contained in:
Nishad Musthafa
2025-06-30 18:06:46 -07:00
parent 2a22fc4080
commit 9ab5ce723d
2 changed files with 81 additions and 9 deletions
+25 -7
View File
@@ -38,14 +38,26 @@ func (s *IOInfoService) matchSIPTrunk(ctx context.Context, trunkID string, call
if trunkID != "" {
// This is a best-effort optimization. Fallthrough to listing trunks if it doesn't work.
if tr, err := s.ss.LoadSIPInboundTrunk(ctx, trunkID); err == nil {
tr, err = sip.MatchTrunkIter(iters.Slice([]*livekit.SIPInboundTrunkInfo{tr}), call)
if err == nil {
return tr, nil
result, err := sip.MatchTrunkDetailed(iters.Slice([]*livekit.SIPInboundTrunkInfo{tr}), call)
if err == nil && result.MatchType != sip.TrunkMatchNone {
return result.Trunk, nil
}
}
}
it := s.SelectSIPInboundTrunk(ctx, call.To.User)
return sip.MatchTrunkIter(it, call)
result, err := sip.MatchTrunkDetailed(it, call)
if err != nil {
return nil, err
}
// If trunks exist but none matched, return a specific error
if result.MatchType == sip.TrunkMatchNone {
return nil, ErrSIPTrunkNotFound
}
// If no trunks were defined at all, return nil (this is different from TrunkMatchNone)
if result.MatchType == sip.TrunkMatchEmpty {
return nil, nil
}
return result.Trunk, nil
}
func (s *IOInfoService) SelectSIPInboundTrunk(ctx context.Context, called string) iters.Iter[*livekit.SIPInboundTrunkInfo] {
@@ -143,12 +155,18 @@ func (s *IOInfoService) GetSIPTrunkAuthentication(ctx context.Context, req *rpc.
}
trunk, err := s.matchSIPTrunk(ctx, "", call)
if err != nil {
// Check if this is the specific "no SIP trunk matched" error
if err == ErrSIPTrunkNotFound {
err := twirp.NotFoundError(fmt.Sprintf("sip trunk not found for destination %q", req.Call.To))
log.Errorw("No SIP trunk matched for auth", err, "sipTrunk", "", "to", req.Call.To)
return nil, err
}
return nil, err
}
if trunk == nil {
err := twirp.NotFoundError(fmt.Sprintf("sip trunk not found for destination %q", req.Call.To))
log.Errorw("No SIP trunk matched for auth", err, "sipTrunk", "", "to", req.Call.To)
return nil, err
// This case is for TrunkMatchEmpty (no trunks defined at all)
// We don't return an error in this case
return nil, nil
}
log.Debugw("SIP trunk matched for auth", "sipTrunk", trunk.SipTrunkId)
return &rpc.GetSIPTrunkAuthenticationResponse{
+56 -2
View File
@@ -141,6 +141,13 @@ func TestGetSIPTrunkAuthentication(t *testing.T) {
AuthUsername: "user2",
AuthPassword: "pass2",
},
{
SipTrunkId: "restricted-trunk",
Numbers: []string{"9999"},
AllowedAddresses: []string{"10.0.0.0/8"}, // Only allow calls from 10.x.x.x
AuthUsername: "restricted-user",
AuthPassword: "restricted-pass",
},
}
for _, tr := range trunks {
@@ -182,11 +189,11 @@ func TestGetSIPTrunkAuthentication(t *testing.T) {
wantErr: false,
},
{
name: "no matching trunk",
name: "trunk exists but source IP not allowed (TrunkMatchNone)",
call: &rpc.SIPCall{
To: &livekit.SIPUri{User: "9999"},
From: &livekit.SIPUri{User: "8888"},
SourceIp: "192.168.1.1",
SourceIp: "192.168.1.1", // Not in 10.0.0.0/8 range
},
wantErr: true,
wantErrMsg: `sip trunk not found for destination "user:\"9999\""`,
@@ -225,3 +232,50 @@ func TestGetSIPTrunkAuthentication(t *testing.T) {
})
}
}
func TestGetSIPTrunkAuthenticationEmpty(t *testing.T) {
ctx := context.Background()
s, _ := ioStoreDocker(t)
// No trunks defined at all
tests := []struct {
name string
call *rpc.SIPCall
wantTrunkID string
wantUser string
wantPass string
wantErr bool
wantErrMsg string
}{
{
name: "no trunks defined (TrunkMatchEmpty)",
call: &rpc.SIPCall{
To: &livekit.SIPUri{User: "9999"},
From: &livekit.SIPUri{User: "8888"},
SourceIp: "192.168.1.1",
},
wantErr: false, // TrunkMatchEmpty - no error
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := &rpc.GetSIPTrunkAuthenticationRequest{
Call: tt.call,
}
resp, err := s.GetSIPTrunkAuthentication(ctx, req)
if tt.wantErr {
require.Error(t, err)
if tt.wantErrMsg != "" {
require.Contains(t, err.Error(), tt.wantErrMsg)
}
return
}
require.NoError(t, err)
require.Nil(t, resp) // For TrunkMatchEmpty, we expect no response
})
}
}