Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose and improve SIP number normalization. #986

Merged
merged 1 commit into from
Feb 25, 2025
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
5 changes: 5 additions & 0 deletions .changeset/sip-num-norm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"github.com/livekit/protocol": minor
---

Expose and improve SIP number normalization.
35 changes: 24 additions & 11 deletions sip/sip.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"maps"
"math"
"net/netip"
"regexp"
"sort"
"strings"

Expand Down Expand Up @@ -201,7 +202,7 @@ func (v *DispatchRuleValidator) Validate(r *livekit.SIPDispatchRuleInfo) error {
}
for _, trunk := range trunks {
for _, number := range numbers {
key := dispatchRuleKey{Pin: pin, Trunk: trunk, Number: normalizeNumber(number)}
key := dispatchRuleKey{Pin: pin, Trunk: trunk, Number: NormalizeNumber(number)}
r2 := v.byRuleKey[key]
if r2 != nil {
v.opt.Conflict(r, r2, DispatchRuleConflictGeneric)
Expand Down Expand Up @@ -273,14 +274,26 @@ func printNumbers(numbers []string) string {
return fmt.Sprintf("%q", numbers)
}

func normalizeNumber(num string) string {
var (
reNumber = regexp.MustCompile(`^\+?[\d\- ()]+$`)
reNumberRepl = strings.NewReplacer(
" ", "",
"-", "",
"(", "",
")", "",
)
)

func NormalizeNumber(num string) string {
if num == "" {
return ""
}
// TODO: Always keep "number" as-is if it's not E.164.
// This will only matter for native SIP clients which have '+' in the username.
if !strings.HasPrefix(num, `+`) {
num = "+" + num
if !reNumber.MatchString(num) {
return num
}
num = reNumberRepl.Replace(num)
if !strings.HasPrefix(num, "+") {
return "+" + num
}
return num
}
Expand All @@ -298,7 +311,7 @@ func validateTrunkInbound(byInbound map[string]*livekit.SIPInboundTrunkInfo, t *
byInbound[""] = t
} else {
for _, num := range t.AllowedNumbers {
inboundKey := normalizeNumber(num)
inboundKey := NormalizeNumber(num)
t2 := byInbound[inboundKey]
if t2 != nil {
opt.Conflict(t, t2, TrunkConflictCallingNumber)
Expand Down Expand Up @@ -425,9 +438,9 @@ func matchNumbers(num string, allowed []string) bool {
if len(allowed) == 0 {
return true
}
num = normalizeNumber(num)
norm := NormalizeNumber(num)
for _, allow := range allowed {
if num == normalizeNumber(allow) {
if num == allow || norm == NormalizeNumber(allow) {
return true
}
}
Expand Down Expand Up @@ -518,7 +531,7 @@ func MatchTrunkIter(it iters.Iter[*livekit.SIPInboundTrunkInfo], srcIP netip.Add
defaultTrunkPrev *livekit.SIPInboundTrunkInfo
defaultTrunkCnt int // to error in case there are multiple ones
)
calledNorm := normalizeNumber(called)
calledNorm := NormalizeNumber(called)
for {
tr, err := it.Next()
if err == io.EOF {
Expand All @@ -542,7 +555,7 @@ func MatchTrunkIter(it iters.Iter[*livekit.SIPInboundTrunkInfo], srcIP netip.Add
defaultTrunkCnt++
} else {
for _, num := range tr.Numbers {
if normalizeNumber(num) == calledNorm {
if num == called || NormalizeNumber(num) == calledNorm {
// Trunk specific to the number.
if selectedTrunk != nil {
opt.Conflict(selectedTrunk, tr, TrunkConflictCalledNumber)
Expand Down
19 changes: 19 additions & 0 deletions sip/sip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,25 @@ import (
"github.com/livekit/protocol/rpc"
)

func TestNormalizeNumber(t *testing.T) {
cases := []struct {
name string
num string
exp string
}{
{"empty", "", ""},
{"number", "123", "+123"},
{"plus", "+123", "+123"},
{"user", "user", "user"},
{"human", "(123) 456 7890", "+1234567890"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
require.Equal(t, c.exp, NormalizeNumber(c.num))
})
}
}

const (
sipNumber1 = "1111 1111"
sipNumber2 = "2222 2222"
Expand Down
Loading