Skip to content

Commit

Permalink
Pass encryption settings and reimplement timeout.
Browse files Browse the repository at this point in the history
  • Loading branch information
dennwc committed Dec 12, 2024
1 parent b33b316 commit f516fb6
Show file tree
Hide file tree
Showing 14 changed files with 197 additions and 84 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/jfreymuth/oggvorbis v1.0.5
github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1
github.com/livekit/mediatransportutil v0.0.0-20241128072814-c363618d4c98
github.com/livekit/protocol v1.28.2-0.20241128072830-b738aedbd841
github.com/livekit/protocol v1.29.5-0.20241212113100-b656ab075ab2 // FIXME: update when PR is merged
github.com/livekit/psrpc v0.6.1-0.20241018124827-1efff3d113a8
github.com/livekit/server-sdk-go/v2 v2.4.0
github.com/livekit/sipgo v0.13.2-0.20241209123643-27500ef99c39
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 h1:jm09419p0lqTkD
github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ=
github.com/livekit/mediatransportutil v0.0.0-20241128072814-c363618d4c98 h1:QA7DqIC/ZSsMj8HC0+zNfMMwssHbA0alZALK68r30LQ=
github.com/livekit/mediatransportutil v0.0.0-20241128072814-c363618d4c98/go.mod h1:WIVFAGzVZ7VMjPC5+nbSfwdFjWcbuLgx97KeNSUDTEo=
github.com/livekit/protocol v1.28.2-0.20241128072830-b738aedbd841 h1:69dSvfL6H6odFhL9q4s+RjDRDdfLY+WUUQ/Lz0av2Bs=
github.com/livekit/protocol v1.28.2-0.20241128072830-b738aedbd841/go.mod h1:mqXSWNHbENjxM0/HG25wZ7wgja/K9fA0PeQxi+MPmWw=
github.com/livekit/protocol v1.29.5-0.20241212113100-b656ab075ab2 h1:+KYhwtahEf3RyfZzk2RozmFwWThSuM64gDfzonJ7XXA=
github.com/livekit/protocol v1.29.5-0.20241212113100-b656ab075ab2/go.mod h1:NDg1btMpKCzr/w6QR5kDuXw/e4Y7yOBE+RUAHsc+Y/M=
github.com/livekit/psrpc v0.6.1-0.20241018124827-1efff3d113a8 h1:Ibh0LoFl5NW5a1KFJEE0eLxxz7dqqKmYTj/BfCb0PbY=
github.com/livekit/psrpc v0.6.1-0.20241018124827-1efff3d113a8/go.mod h1:CQUBSPfYYAaevg1TNCc6/aYsa8DJH4jSRFdCeSZk5u0=
github.com/livekit/server-sdk-go/v2 v2.4.0 h1:ide41hppBf7btHLz/nj6rLIQSkaIOxP5tVSki74ZDhg=
Expand Down
1 change: 1 addition & 0 deletions pkg/media/rtp/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ type UDPConn interface {
Close() error
}

// Deprecated: use MediaPort instead
type Conn struct {
log logger.Logger
wmu sync.Mutex
Expand Down
32 changes: 23 additions & 9 deletions pkg/media/sdp/offer.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ import (
"github.com/livekit/sip/pkg/media/srtp"
)

type Encryption int

const (
EncryptionNone Encryption = iota
EncryptionAllow
EncryptionRequire
)

type CodecInfo struct {
Type byte
Codec media.Codec
Expand Down Expand Up @@ -92,7 +100,7 @@ func appendCryptoProfiles(attrs []sdp.Attribute, profiles []srtp.Profile) []sdp.
return attrs
}

func OfferMedia(rtpListenerPort int, encrypted bool) (MediaDesc, *sdp.MediaDescription, error) {
func OfferMedia(rtpListenerPort int, encrypted Encryption) (MediaDesc, *sdp.MediaDescription, error) {
// Static compiler check for frame duration hardcoded below.
var _ = [1]struct{}{}[20*time.Millisecond-rtp.DefFrameDur]

Expand All @@ -117,7 +125,7 @@ func OfferMedia(rtpListenerPort int, encrypted bool) (MediaDesc, *sdp.MediaDescr
})
}
var cryptoProfiles []srtp.Profile
if encrypted {
if encrypted != EncryptionNone {
var err error
cryptoProfiles, err = srtp.DefaultProfiles()
if err != nil {
Expand All @@ -132,7 +140,7 @@ func OfferMedia(rtpListenerPort int, encrypted bool) (MediaDesc, *sdp.MediaDescr
}...)

proto := "AVP"
if encrypted {
if encrypted != EncryptionNone {
proto = "SAVP"
}

Expand Down Expand Up @@ -198,7 +206,7 @@ type Offer Description

type Answer Description

func NewOffer(publicIp netip.Addr, rtpListenerPort int, encrypted bool) (*Offer, error) {
func NewOffer(publicIp netip.Addr, rtpListenerPort int, encrypted Encryption) (*Offer, error) {
sessId := rand.Uint64() // TODO: do we need to track these?

m, mediaDesc, err := OfferMedia(rtpListenerPort, encrypted)
Expand Down Expand Up @@ -238,7 +246,7 @@ func NewOffer(publicIp netip.Addr, rtpListenerPort int, encrypted bool) (*Offer,
}, nil
}

func (d *Offer) Answer(publicIp netip.Addr, rtpListenerPort int) (*Answer, *MediaConfig, error) {
func (d *Offer) Answer(publicIp netip.Addr, rtpListenerPort int, enc Encryption) (*Answer, *MediaConfig, error) {
audio, err := SelectAudio(d.MediaDesc)
if err != nil {
return nil, nil, err
Expand All @@ -248,7 +256,7 @@ func (d *Offer) Answer(publicIp netip.Addr, rtpListenerPort int) (*Answer, *Medi
sconf *srtp.Config
sprof *srtp.Profile
)
if len(d.CryptoProfiles) != 0 {
if len(d.CryptoProfiles) != 0 && enc != EncryptionNone {
answer, err := srtp.DefaultProfiles()
if err != nil {
return nil, nil, err
Expand All @@ -257,6 +265,9 @@ func (d *Offer) Answer(publicIp netip.Addr, rtpListenerPort int) (*Answer, *Medi
if err != nil {
return nil, nil, err
}
if sprof == nil && enc == EncryptionRequire {
return nil, nil, errors.New("no common encryption profiles")
}
}

mediaDesc := AnswerMedia(rtpListenerPort, audio, sprof)
Expand Down Expand Up @@ -304,17 +315,20 @@ func (d *Offer) Answer(publicIp netip.Addr, rtpListenerPort int) (*Answer, *Medi
}, nil
}

func (d *Answer) Apply(offer *Offer) (*MediaConfig, error) {
func (d *Answer) Apply(offer *Offer, enc Encryption) (*MediaConfig, error) {
audio, err := SelectAudio(d.MediaDesc)
if err != nil {
return nil, err
}
var sconf *srtp.Config
if len(d.CryptoProfiles) != 0 {
if len(d.CryptoProfiles) != 0 && enc != EncryptionNone {
sconf, _, err = SelectCrypto(offer.CryptoProfiles, d.CryptoProfiles, false)
if err != nil {
return nil, err
}
if sconf == nil && enc == EncryptionRequire {
return nil, errors.New("no common encryption profiles")
}
}
return &MediaConfig{
Local: offer.Addr,
Expand Down Expand Up @@ -515,5 +529,5 @@ func SelectCrypto(offer, answer []srtp.Profile, swap bool) (*srtp.Config, *srtp.
return c, prof, nil
}
}
return nil, nil, errors.New("no common crypto")
return nil, nil, nil
}
8 changes: 4 additions & 4 deletions pkg/media/sdp/offer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func getInline(s string) string {

func TestSDPMediaOffer(t *testing.T) {
const port = 12345
_, offer, err := OfferMedia(port, false)
_, offer, err := OfferMedia(port, EncryptionNone)
require.NoError(t, err)
require.Equal(t, &sdp.MediaDescription{
MediaName: sdp.MediaName{
Expand All @@ -61,7 +61,7 @@ func TestSDPMediaOffer(t *testing.T) {
},
}, offer)

_, offer, err = OfferMedia(port, true)
_, offer, err = OfferMedia(port, EncryptionRequire)
require.NoError(t, err)
i := slices.IndexFunc(offer.Attributes, func(a sdp.Attribute) bool {
return a.Key == "crypto"
Expand Down Expand Up @@ -92,7 +92,7 @@ func TestSDPMediaOffer(t *testing.T) {
media.CodecSetEnabled(g722.SDPName, false)
defer media.CodecSetEnabled(g722.SDPName, true)

_, offer, err = OfferMedia(port, false)
_, offer, err = OfferMedia(port, EncryptionNone)
require.NoError(t, err)
require.Equal(t, &sdp.MediaDescription{
MediaName: sdp.MediaName{
Expand Down Expand Up @@ -272,7 +272,7 @@ func TestSDPMediaAnswer(t *testing.T) {
require.Equal(t, c.exp, got)
})
}
_, offer, err := OfferMedia(port, false)
_, offer, err := OfferMedia(port, EncryptionNone)
require.NoError(t, err)
require.Equal(t, &sdp.MediaDescription{
MediaName: sdp.MediaName{
Expand Down
19 changes: 11 additions & 8 deletions pkg/service/psrpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/rpc"
"github.com/livekit/protocol/tracer"

"github.com/livekit/sip/pkg/sip"
)

Expand Down Expand Up @@ -77,10 +76,11 @@ func DispatchCall(ctx context.Context, psrpcClient rpc.IOInfoClient, log logger.
case rpc.SIPDispatchResult_LEGACY_ACCEPT_OR_PIN:
if resp.RequestPin {
return sip.CallDispatch{
ProjectID: resp.ProjectId,
TrunkID: resp.SipTrunkId,
DispatchRuleID: resp.SipDispatchRuleId,
Result: sip.DispatchRequestPin,
ProjectID: resp.ProjectId,
TrunkID: resp.SipTrunkId,
DispatchRuleID: resp.SipDispatchRuleId,
Result: sip.DispatchRequestPin,
MediaEncryption: resp.MediaEncryption,
}
}
// TODO: finally deprecate and drop
Expand All @@ -105,6 +105,7 @@ func DispatchCall(ctx context.Context, psrpcClient rpc.IOInfoClient, log logger.
EnabledFeatures: resp.EnabledFeatures,
RingingTimeout: resp.RingingTimeout.AsDuration(),
MaxCallDuration: resp.MaxCallDuration.AsDuration(),
MediaEncryption: resp.MediaEncryption,
}
case rpc.SIPDispatchResult_ACCEPT:
return sip.CallDispatch{
Expand All @@ -128,12 +129,14 @@ func DispatchCall(ctx context.Context, psrpcClient rpc.IOInfoClient, log logger.
EnabledFeatures: resp.EnabledFeatures,
RingingTimeout: resp.RingingTimeout.AsDuration(),
MaxCallDuration: resp.MaxCallDuration.AsDuration(),
MediaEncryption: resp.MediaEncryption,
}
case rpc.SIPDispatchResult_REQUEST_PIN:
return sip.CallDispatch{
ProjectID: resp.ProjectId,
Result: sip.DispatchRequestPin,
TrunkID: resp.SipTrunkId,
ProjectID: resp.ProjectId,
Result: sip.DispatchRequestPin,
TrunkID: resp.SipTrunkId,
MediaEncryption: resp.MediaEncryption,
}
case rpc.SIPDispatchResult_REJECT:
return sip.CallDispatch{
Expand Down
5 changes: 5 additions & 0 deletions pkg/sip/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ func (c *Client) createSIPParticipant(ctx context.Context, req *rpc.InternalCrea
if req.SipTrunkId != "" {
log = log.WithValues("sipTrunk", req.SipTrunkId)
}
enc, err := sdpEncryption(req.MediaEncryption)
if err != nil {
return nil, err
}
log = log.WithValues(
"callID", req.SipCallId,
"room", req.RoomName,
Expand Down Expand Up @@ -215,6 +219,7 @@ func (c *Client) createSIPParticipant(ctx context.Context, req *rpc.InternalCrea
ringingTimeout: req.RingingTimeout.AsDuration(),
maxCallDuration: req.MaxCallDuration.AsDuration(),
enabledFeatures: req.EnabledFeatures,
mediaEncryption: enc,
}
log.Infow("Creating SIP participant")
call, err := c.newCall(ctx, c.conf, log, LocalTag(req.SipCallId), roomConf, sipConf, callInfo, ioClient)
Expand Down
52 changes: 38 additions & 14 deletions pkg/sip/inbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,17 +412,21 @@ func (c *inboundCall) handleInvite(ctx context.Context, req *sip.Request, trunkI
pinPrompt = true
}

// We need to start media first, otherwise we won't be able to send audio prompts to the caller, or receive DTMF.
answerData, err := c.runMediaConn(req.Body(), conf, disp.EnabledFeatures)
if err != nil {
c.log.Errorw("Cannot start media", err)
c.cc.RespondAndDrop(sip.StatusInternalServerError, "")
c.close(true, callDropped, "media-failed")
return err
runMedia := func(enc livekit.SIPMediaEncryption) ([]byte, error) {
answerData, err := c.runMediaConn(req.Body(), enc, conf, disp.EnabledFeatures)
if err != nil {
c.log.Errorw("Cannot start media", err)
c.cc.RespondAndDrop(sip.StatusInternalServerError, "")
c.close(true, callDropped, "media-failed")
return nil, err
}
return answerData, nil
}
acceptCall := func() (bool, error) {

// We need to start media first, otherwise we won't be able to send audio prompts to the caller, or receive DTMF.
acceptCall := func(answerData []byte) (bool, error) {
c.log.Infow("Accepting the call", "headers", disp.Headers)
if err = c.cc.Accept(ctx, answerData, disp.Headers); err != nil {
if err := c.cc.Accept(ctx, answerData, disp.Headers); err != nil {
c.log.Errorw("Cannot respond to INVITE", err)
return false, err
}
Expand All @@ -435,15 +439,30 @@ func (c *inboundCall) handleInvite(ctx context.Context, req *sip.Request, trunkI
}

ok := false
var answerData []byte
if pinPrompt {
var err error
// Accept the call first on the SIP side, so that we can send audio prompts.
if ok, err = acceptCall(); !ok {
// This also means we have to pick encryption setting early, before room is selected.
// Backend must explicitly enable encryption for pin prompts.
answerData, err = runMedia(disp.MediaEncryption)
if err != nil {
return err // already sent a response
}
if ok, err = acceptCall(answerData); !ok {
return err // could be success if the caller hung up
}
disp, ok, err = c.pinPrompt(ctx, trunkID)
if !ok {
return err // already sent a response. Could be success if user hung up
}
} else {
// Start media with given encryption settings.
var err error
answerData, err = runMedia(disp.MediaEncryption)
if err != nil {
return err // already sent a response
}
}
if len(disp.HeadersToAttributes) != 0 {
p := &disp.Room.Participant
Expand Down Expand Up @@ -486,7 +505,7 @@ func (c *inboundCall) handleInvite(ctx context.Context, req *sip.Request, trunkI
if ok, err := c.waitSubscribe(ctx, disp.RingingTimeout); !ok {
return err // already sent a response. Could be success if caller hung up
}
if ok, err := acceptCall(); !ok {
if ok, err := acceptCall(answerData); !ok {
return err // already sent a response. Could be success if caller hung up
}
}
Expand Down Expand Up @@ -518,11 +537,16 @@ func (c *inboundCall) handleInvite(ctx context.Context, req *sip.Request, trunkI
}
}

func (c *inboundCall) runMediaConn(offerData []byte, conf *config.Config, features []livekit.SIPFeature) (answerData []byte, _ error) {
func (c *inboundCall) runMediaConn(offerData []byte, enc livekit.SIPMediaEncryption, conf *config.Config, features []livekit.SIPFeature) (answerData []byte, _ error) {
c.mon.SDPSize(len(offerData), true)
c.log.Debugw("SDP offer", "sdp", string(offerData))
e, err := sdpEncryption(enc)
if err != nil {
c.log.Errorw("Cannot parse encryption", err)
return nil, err
}

mp, err := NewMediaPort(c.log, c.mon, &MediaConfig{
mp, err := NewMediaPort(c.log, c.mon, &MediaOptions{
IP: c.s.sconf.SignalingIP,
Ports: conf.RTPPort,
MediaTimeoutInitial: c.s.conf.MediaTimeoutInitial,
Expand All @@ -535,7 +559,7 @@ func (c *inboundCall) runMediaConn(offerData []byte, conf *config.Config, featur
c.media.EnableTimeout(false) // enabled once we accept the call
c.media.SetDTMFAudio(conf.AudioDTMF)

answer, mconf, err := mp.SetOffer(offerData)
answer, mconf, err := mp.SetOffer(offerData, e)
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit f516fb6

Please sign in to comment.