Skip to content

Commit

Permalink
Expose and improve SIP number normalization. (#986)
Browse files Browse the repository at this point in the history
  • Loading branch information
dennwc authored Feb 25, 2025
1 parent 8819517 commit f18a2d1
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 11 deletions.
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

0 comments on commit f18a2d1

Please sign in to comment.