Skip to content

Commit

Permalink
Move SIP call dispatch info into a separate type.
Browse files Browse the repository at this point in the history
  • Loading branch information
dennwc committed Feb 26, 2025
1 parent 715433b commit 92dbd2a
Show file tree
Hide file tree
Showing 9 changed files with 1,176 additions and 930 deletions.
6 changes: 6 additions & 0 deletions .changeset/sip-dispatch-info.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@livekit/protocol": minor
"github.com/livekit/protocol": minor
---

Move SIP call dispatch info into s separate type.
688 changes: 395 additions & 293 deletions livekit/livekit_sip.pb.go

Large diffs are not rendered by default.

473 changes: 239 additions & 234 deletions livekit/livekit_sip.twirp.go

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions protobufs/livekit_sip.proto
Original file line number Diff line number Diff line change
Expand Up @@ -671,3 +671,12 @@ enum SIPCallDirection {
SCD_INBOUND = 1;
SCD_OUTBOUND = 2;
}

message SIPCall {
string lk_call_id = 1;
string source_ip = 2; // source ip (without port)
SIPUri address = 3; // address in the request line (INVITE)
SIPUri from = 4; // From header
SIPUri to = 5; // To header
repeated SIPUri via = 6; // Via headers
}
32 changes: 18 additions & 14 deletions protobufs/rpc/io.proto
Original file line number Diff line number Diff line change
Expand Up @@ -74,22 +74,24 @@ message UpdateIngressStateRequest {
}

message GetSIPTrunkAuthenticationRequest {
string sip_call_id = 6;
string sip_call_id = 6 [deprecated=true];

// What Number is calling
string from = 2;
string from = 2 [deprecated=true];
// What Host is calling
string from_host = 7;
string from_host = 7 [deprecated=true];

// What Number was called
string to = 3;
string to = 3 [deprecated=true];
// What Host was called
string to_host = 5;
string to_host = 5 [deprecated=true];

// What is the IP address of the called number
string src_address = 4;
string src_address = 4 [deprecated=true];

// NEXT ID: 8
livekit.SIPCall call = 8;

// NEXT ID: 9
}

message GetSIPTrunkAuthenticationResponse {
Expand All @@ -104,22 +106,22 @@ message GetSIPTrunkAuthenticationResponse {
}

message EvaluateSIPDispatchRulesRequest {
string sip_call_id = 8;
string sip_call_id = 8 [deprecated=true];
string sip_participant_id = 1 [deprecated=true];
// Trunk from the auth response, if any
string sip_trunk_id = 10;

// What Number is calling
string calling_number = 2;
string calling_number = 2 [deprecated=true];

// What Host is calling
string calling_host = 11;
string calling_host = 11 [deprecated=true];

// What Number was called
string called_number = 3;
string called_number = 3 [deprecated=true];

// What is the IP address of the called number
string src_address = 4;
string src_address = 4 [deprecated=true];

// What pin has been entered if any
string pin = 5;
Expand All @@ -128,13 +130,15 @@ message EvaluateSIPDispatchRulesRequest {
bool no_pin = 6;

// What Host was called
string called_host = 7;
string called_host = 7 [deprecated=true];

// Extra participant attributes added for this call.
// Usually include provider-specific metadata.
map<string, string> extra_attributes = 9;

// NEXT ID: 12
livekit.SIPCall call = 12;

// NEXT ID: 13
}

message EvaluateSIPDispatchRulesResponse {
Expand Down
599 changes: 330 additions & 269 deletions rpc/io.pb.go

Large diffs are not rendered by default.

200 changes: 101 additions & 99 deletions rpc/io.psrpc.go

Large diffs are not rendered by default.

55 changes: 55 additions & 0 deletions rpc/sip.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,66 @@ import (
"errors"
"maps"
"math/rand/v2"
"net"
"strings"

"github.com/livekit/protocol/livekit"
)

func (p *GetSIPTrunkAuthenticationRequest) SIPCall() *livekit.SIPCall {
if p == nil {
return nil
}
if p.Call != nil {
return p.Call
}
ip := p.SrcAddress
if addr, _, err := net.SplitHostPort(ip); err == nil {
ip = addr
}
c := &livekit.SIPCall{
LkCallId: p.SipCallId,
SourceIp: ip,
From: &livekit.SIPUri{
User: p.From,
Host: p.FromHost,
},
To: &livekit.SIPUri{
User: p.To,
Host: p.ToHost,
},
}
c.Address = c.To
return c
}

func (p *EvaluateSIPDispatchRulesRequest) SIPCall() *livekit.SIPCall {
if p == nil {
return nil
}
if p.Call != nil {
return p.Call
}
ip := p.SrcAddress
if addr, _, err := net.SplitHostPort(ip); err == nil {
ip = addr
}
c := &livekit.SIPCall{
LkCallId: p.SipCallId,
SourceIp: ip,
From: &livekit.SIPUri{
User: p.CallingNumber,
Host: p.CallingHost,
},
To: &livekit.SIPUri{
User: p.CalledNumber,
Host: p.CalledHost,
},
}
c.Address = c.To
return c
}

// NewCreateSIPParticipantRequest fills InternalCreateSIPParticipantRequest from
// livekit.CreateSIPParticipantRequest and livekit.SIPTrunkInfo.
func NewCreateSIPParticipantRequest(
Expand Down
44 changes: 23 additions & 21 deletions sip/sip.go
Original file line number Diff line number Diff line change
Expand Up @@ -419,16 +419,17 @@ func matchAddrMask(ip netip.Addr, mask string) bool {
return pref.Contains(ip)
}

func matchAddrMasks(addr netip.Addr, host string, masks []string) bool {
if !addr.IsValid() {
func matchAddrMasks(addr string, host string, masks []string) bool {
ip, err := netip.ParseAddr(addr)
if err != nil {
return true
}
masks = filterInvalidAddrMasks(masks)
if len(masks) == 0 {
return true
}
for _, mask := range masks {
if mask == host || matchAddrMask(addr, mask) {
if mask == host || matchAddrMask(ip, mask) {
return true
}
}
Expand All @@ -452,8 +453,8 @@ func matchNumbers(num string, allowed []string) bool {
// Returns nil if no rules matched or an error if there are conflicting definitions.
//
// Deprecated: use MatchTrunkIter
func MatchTrunk(trunks []*livekit.SIPInboundTrunkInfo, srcIP netip.Addr, calling, called string, callingHost string, opts ...MatchTrunkOpt) (*livekit.SIPInboundTrunkInfo, error) {
return MatchTrunkIter(iters.Slice(trunks), srcIP, calling, called, callingHost, opts...)
func MatchTrunk(trunks []*livekit.SIPInboundTrunkInfo, call *livekit.SIPCall, opts ...MatchTrunkOpt) (*livekit.SIPInboundTrunkInfo, error) {
return MatchTrunkIter(iters.Slice(trunks), call, opts...)
}

type matchTrunkOpts struct {
Expand Down Expand Up @@ -519,7 +520,7 @@ func WithTrunkConflict(fnc TrunkConflictFunc) MatchTrunkOpt {

// MatchTrunkIter finds a SIP Trunk definition matching the request.
// Returns nil if no rules matched or an error if there are conflicting definitions.
func MatchTrunkIter(it iters.Iter[*livekit.SIPInboundTrunkInfo], srcIP netip.Addr, calling, called string, callingHost string, opts ...MatchTrunkOpt) (*livekit.SIPInboundTrunkInfo, error) {
func MatchTrunkIter(it iters.Iter[*livekit.SIPInboundTrunkInfo], call *livekit.SIPCall, opts ...MatchTrunkOpt) (*livekit.SIPInboundTrunkInfo, error) {
defer it.Close()
var opt matchTrunkOpts
for _, fnc := range opts {
Expand All @@ -532,7 +533,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(call.To.User)
for {
tr, err := it.Next()
if err == io.EOF {
Expand All @@ -541,11 +542,11 @@ func MatchTrunkIter(it iters.Iter[*livekit.SIPInboundTrunkInfo], srcIP netip.Add
return nil, err
}
// Do not consider it if number doesn't match.
if !matchNumbers(calling, tr.AllowedNumbers) {
if !matchNumbers(call.From.User, tr.AllowedNumbers) {
opt.Filtered(tr, TrunkFilteredCallingNumberDisallowed)
continue
}
if !matchAddrMasks(srcIP, callingHost, tr.AllowedAddresses) {
if !matchAddrMasks(call.SourceIp, call.From.Host, tr.AllowedAddresses) {
opt.Filtered(tr, TrunkFilteredSourceAddressDisallowed)
continue
}
Expand All @@ -556,15 +557,15 @@ func MatchTrunkIter(it iters.Iter[*livekit.SIPInboundTrunkInfo], srcIP netip.Add
defaultTrunkCnt++
} else {
for _, num := range tr.Numbers {
if num == called || NormalizeNumber(num) == calledNorm {
if num == call.To.User || NormalizeNumber(num) == calledNorm {
// Trunk specific to the number.
if selectedTrunk != nil {
opt.Conflict(selectedTrunk, tr, TrunkConflictCalledNumber)
if opt.AllowConflicts {
// This path is unreachable, since we pick the first trunk. Kept for completeness.
continue
}
return nil, twirp.NewErrorf(twirp.FailedPrecondition, "Multiple SIP Trunks matched for %q", called)
return nil, twirp.NewErrorf(twirp.FailedPrecondition, "Multiple SIP Trunks matched for %q", call.To.User)
}
selectedTrunk = tr
if opt.AllowConflicts {
Expand All @@ -584,7 +585,7 @@ func MatchTrunkIter(it iters.Iter[*livekit.SIPInboundTrunkInfo], srcIP netip.Add
if defaultTrunkCnt > 1 {
opt.Conflict(defaultTrunk, defaultTrunkPrev, TrunkConflictDefault)
if !opt.AllowConflicts {
return nil, twirp.NewErrorf(twirp.FailedPrecondition, "Multiple default SIP Trunks matched for %q", called)
return nil, twirp.NewErrorf(twirp.FailedPrecondition, "Multiple default SIP Trunks matched for %q", call.To.User)
}
}
// Could still be nil here.
Expand Down Expand Up @@ -721,6 +722,7 @@ func MatchDispatchRuleIter(trunk *livekit.SIPInboundTrunkInfo, rules iters.Iter[

// EvaluateDispatchRule checks a selected Dispatch Rule against the provided request.
func EvaluateDispatchRule(projectID string, trunk *livekit.SIPInboundTrunkInfo, rule *livekit.SIPDispatchRuleInfo, req *rpc.EvaluateSIPDispatchRulesRequest) (*rpc.EvaluateSIPDispatchRulesResponse, error) {
call := req.SIPCall()
sentPin := req.GetPin()

trunkID := req.SipTrunkId
Expand All @@ -738,16 +740,16 @@ func EvaluateDispatchRule(projectID string, trunk *livekit.SIPInboundTrunkInfo,
for k, v := range req.ExtraAttributes {
attrs[k] = v
}
attrs[livekit.AttrSIPCallID] = req.SipCallId
attrs[livekit.AttrSIPCallID] = call.LkCallId
attrs[livekit.AttrSIPTrunkID] = trunkID

to := req.CalledNumber
from := req.CallingNumber
fromName := "Phone " + req.CallingNumber
fromID := "sip_" + req.CallingNumber
to := call.To.User
from := call.From.User
fromName := "Phone " + from
fromID := "sip_" + from
if rule.HidePhoneNumber {
// Mask the phone number, hash identity. Omit number in attrs.
h := sha256.Sum256([]byte(req.CallingNumber))
h := sha256.Sum256([]byte(call.From.User))
fromID = "sip_" + hex.EncodeToString(h[:8])
// TODO: Maybe keep regional code, but mask all but 4 last digits?
n := 4
Expand All @@ -757,9 +759,9 @@ func EvaluateDispatchRule(projectID string, trunk *livekit.SIPInboundTrunkInfo,
from = from[len(from)-n:]
fromName = "Phone " + from
} else {
attrs[livekit.AttrSIPPhoneNumber] = req.CallingNumber
attrs[livekit.AttrSIPHostName] = req.CallingHost
attrs[livekit.AttrSIPTrunkNumber] = req.CalledNumber
attrs[livekit.AttrSIPPhoneNumber] = call.From.User
attrs[livekit.AttrSIPHostName] = call.From.Host
attrs[livekit.AttrSIPTrunkNumber] = call.To.User
}

room, rulePin, err := GetPinAndRoom(rule)
Expand Down

0 comments on commit 92dbd2a

Please sign in to comment.