Skip to content

Commit f516fb6

Browse files
committed
Pass encryption settings and reimplement timeout.
1 parent b33b316 commit f516fb6

14 files changed

+197
-84
lines changed

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require (
1212
github.com/jfreymuth/oggvorbis v1.0.5
1313
github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1
1414
github.com/livekit/mediatransportutil v0.0.0-20241128072814-c363618d4c98
15-
github.com/livekit/protocol v1.28.2-0.20241128072830-b738aedbd841
15+
github.com/livekit/protocol v1.29.5-0.20241212113100-b656ab075ab2 // FIXME: update when PR is merged
1616
github.com/livekit/psrpc v0.6.1-0.20241018124827-1efff3d113a8
1717
github.com/livekit/server-sdk-go/v2 v2.4.0
1818
github.com/livekit/sipgo v0.13.2-0.20241209123643-27500ef99c39

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,8 @@ github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 h1:jm09419p0lqTkD
122122
github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ=
123123
github.com/livekit/mediatransportutil v0.0.0-20241128072814-c363618d4c98 h1:QA7DqIC/ZSsMj8HC0+zNfMMwssHbA0alZALK68r30LQ=
124124
github.com/livekit/mediatransportutil v0.0.0-20241128072814-c363618d4c98/go.mod h1:WIVFAGzVZ7VMjPC5+nbSfwdFjWcbuLgx97KeNSUDTEo=
125-
github.com/livekit/protocol v1.28.2-0.20241128072830-b738aedbd841 h1:69dSvfL6H6odFhL9q4s+RjDRDdfLY+WUUQ/Lz0av2Bs=
126-
github.com/livekit/protocol v1.28.2-0.20241128072830-b738aedbd841/go.mod h1:mqXSWNHbENjxM0/HG25wZ7wgja/K9fA0PeQxi+MPmWw=
125+
github.com/livekit/protocol v1.29.5-0.20241212113100-b656ab075ab2 h1:+KYhwtahEf3RyfZzk2RozmFwWThSuM64gDfzonJ7XXA=
126+
github.com/livekit/protocol v1.29.5-0.20241212113100-b656ab075ab2/go.mod h1:NDg1btMpKCzr/w6QR5kDuXw/e4Y7yOBE+RUAHsc+Y/M=
127127
github.com/livekit/psrpc v0.6.1-0.20241018124827-1efff3d113a8 h1:Ibh0LoFl5NW5a1KFJEE0eLxxz7dqqKmYTj/BfCb0PbY=
128128
github.com/livekit/psrpc v0.6.1-0.20241018124827-1efff3d113a8/go.mod h1:CQUBSPfYYAaevg1TNCc6/aYsa8DJH4jSRFdCeSZk5u0=
129129
github.com/livekit/server-sdk-go/v2 v2.4.0 h1:ide41hppBf7btHLz/nj6rLIQSkaIOxP5tVSki74ZDhg=

pkg/media/rtp/conn.go

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ type UDPConn interface {
7575
Close() error
7676
}
7777

78+
// Deprecated: use MediaPort instead
7879
type Conn struct {
7980
log logger.Logger
8081
wmu sync.Mutex

pkg/media/sdp/offer.go

+23-9
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ import (
3333
"github.com/livekit/sip/pkg/media/srtp"
3434
)
3535

36+
type Encryption int
37+
38+
const (
39+
EncryptionNone Encryption = iota
40+
EncryptionAllow
41+
EncryptionRequire
42+
)
43+
3644
type CodecInfo struct {
3745
Type byte
3846
Codec media.Codec
@@ -92,7 +100,7 @@ func appendCryptoProfiles(attrs []sdp.Attribute, profiles []srtp.Profile) []sdp.
92100
return attrs
93101
}
94102

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

@@ -117,7 +125,7 @@ func OfferMedia(rtpListenerPort int, encrypted bool) (MediaDesc, *sdp.MediaDescr
117125
})
118126
}
119127
var cryptoProfiles []srtp.Profile
120-
if encrypted {
128+
if encrypted != EncryptionNone {
121129
var err error
122130
cryptoProfiles, err = srtp.DefaultProfiles()
123131
if err != nil {
@@ -132,7 +140,7 @@ func OfferMedia(rtpListenerPort int, encrypted bool) (MediaDesc, *sdp.MediaDescr
132140
}...)
133141

134142
proto := "AVP"
135-
if encrypted {
143+
if encrypted != EncryptionNone {
136144
proto = "SAVP"
137145
}
138146

@@ -198,7 +206,7 @@ type Offer Description
198206

199207
type Answer Description
200208

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

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

241-
func (d *Offer) Answer(publicIp netip.Addr, rtpListenerPort int) (*Answer, *MediaConfig, error) {
249+
func (d *Offer) Answer(publicIp netip.Addr, rtpListenerPort int, enc Encryption) (*Answer, *MediaConfig, error) {
242250
audio, err := SelectAudio(d.MediaDesc)
243251
if err != nil {
244252
return nil, nil, err
@@ -248,7 +256,7 @@ func (d *Offer) Answer(publicIp netip.Addr, rtpListenerPort int) (*Answer, *Medi
248256
sconf *srtp.Config
249257
sprof *srtp.Profile
250258
)
251-
if len(d.CryptoProfiles) != 0 {
259+
if len(d.CryptoProfiles) != 0 && enc != EncryptionNone {
252260
answer, err := srtp.DefaultProfiles()
253261
if err != nil {
254262
return nil, nil, err
@@ -257,6 +265,9 @@ func (d *Offer) Answer(publicIp netip.Addr, rtpListenerPort int) (*Answer, *Medi
257265
if err != nil {
258266
return nil, nil, err
259267
}
268+
if sprof == nil && enc == EncryptionRequire {
269+
return nil, nil, errors.New("no common encryption profiles")
270+
}
260271
}
261272

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

307-
func (d *Answer) Apply(offer *Offer) (*MediaConfig, error) {
318+
func (d *Answer) Apply(offer *Offer, enc Encryption) (*MediaConfig, error) {
308319
audio, err := SelectAudio(d.MediaDesc)
309320
if err != nil {
310321
return nil, err
311322
}
312323
var sconf *srtp.Config
313-
if len(d.CryptoProfiles) != 0 {
324+
if len(d.CryptoProfiles) != 0 && enc != EncryptionNone {
314325
sconf, _, err = SelectCrypto(offer.CryptoProfiles, d.CryptoProfiles, false)
315326
if err != nil {
316327
return nil, err
317328
}
329+
if sconf == nil && enc == EncryptionRequire {
330+
return nil, errors.New("no common encryption profiles")
331+
}
318332
}
319333
return &MediaConfig{
320334
Local: offer.Addr,
@@ -515,5 +529,5 @@ func SelectCrypto(offer, answer []srtp.Profile, swap bool) (*srtp.Config, *srtp.
515529
return c, prof, nil
516530
}
517531
}
518-
return nil, nil, errors.New("no common crypto")
532+
return nil, nil, nil
519533
}

pkg/media/sdp/offer_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func getInline(s string) string {
4141

4242
func TestSDPMediaOffer(t *testing.T) {
4343
const port = 12345
44-
_, offer, err := OfferMedia(port, false)
44+
_, offer, err := OfferMedia(port, EncryptionNone)
4545
require.NoError(t, err)
4646
require.Equal(t, &sdp.MediaDescription{
4747
MediaName: sdp.MediaName{
@@ -61,7 +61,7 @@ func TestSDPMediaOffer(t *testing.T) {
6161
},
6262
}, offer)
6363

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

95-
_, offer, err = OfferMedia(port, false)
95+
_, offer, err = OfferMedia(port, EncryptionNone)
9696
require.NoError(t, err)
9797
require.Equal(t, &sdp.MediaDescription{
9898
MediaName: sdp.MediaName{
@@ -272,7 +272,7 @@ func TestSDPMediaAnswer(t *testing.T) {
272272
require.Equal(t, c.exp, got)
273273
})
274274
}
275-
_, offer, err := OfferMedia(port, false)
275+
_, offer, err := OfferMedia(port, EncryptionNone)
276276
require.NoError(t, err)
277277
require.Equal(t, &sdp.MediaDescription{
278278
MediaName: sdp.MediaName{

pkg/service/psrpc.go

+11-8
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"github.com/livekit/protocol/logger"
99
"github.com/livekit/protocol/rpc"
1010
"github.com/livekit/protocol/tracer"
11-
1211
"github.com/livekit/sip/pkg/sip"
1312
)
1413

@@ -77,10 +76,11 @@ func DispatchCall(ctx context.Context, psrpcClient rpc.IOInfoClient, log logger.
7776
case rpc.SIPDispatchResult_LEGACY_ACCEPT_OR_PIN:
7877
if resp.RequestPin {
7978
return sip.CallDispatch{
80-
ProjectID: resp.ProjectId,
81-
TrunkID: resp.SipTrunkId,
82-
DispatchRuleID: resp.SipDispatchRuleId,
83-
Result: sip.DispatchRequestPin,
79+
ProjectID: resp.ProjectId,
80+
TrunkID: resp.SipTrunkId,
81+
DispatchRuleID: resp.SipDispatchRuleId,
82+
Result: sip.DispatchRequestPin,
83+
MediaEncryption: resp.MediaEncryption,
8484
}
8585
}
8686
// TODO: finally deprecate and drop
@@ -105,6 +105,7 @@ func DispatchCall(ctx context.Context, psrpcClient rpc.IOInfoClient, log logger.
105105
EnabledFeatures: resp.EnabledFeatures,
106106
RingingTimeout: resp.RingingTimeout.AsDuration(),
107107
MaxCallDuration: resp.MaxCallDuration.AsDuration(),
108+
MediaEncryption: resp.MediaEncryption,
108109
}
109110
case rpc.SIPDispatchResult_ACCEPT:
110111
return sip.CallDispatch{
@@ -128,12 +129,14 @@ func DispatchCall(ctx context.Context, psrpcClient rpc.IOInfoClient, log logger.
128129
EnabledFeatures: resp.EnabledFeatures,
129130
RingingTimeout: resp.RingingTimeout.AsDuration(),
130131
MaxCallDuration: resp.MaxCallDuration.AsDuration(),
132+
MediaEncryption: resp.MediaEncryption,
131133
}
132134
case rpc.SIPDispatchResult_REQUEST_PIN:
133135
return sip.CallDispatch{
134-
ProjectID: resp.ProjectId,
135-
Result: sip.DispatchRequestPin,
136-
TrunkID: resp.SipTrunkId,
136+
ProjectID: resp.ProjectId,
137+
Result: sip.DispatchRequestPin,
138+
TrunkID: resp.SipTrunkId,
139+
MediaEncryption: resp.MediaEncryption,
137140
}
138141
case rpc.SIPDispatchResult_REJECT:
139142
return sip.CallDispatch{

pkg/sip/client.go

+5
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ func (c *Client) createSIPParticipant(ctx context.Context, req *rpc.InternalCrea
158158
if req.SipTrunkId != "" {
159159
log = log.WithValues("sipTrunk", req.SipTrunkId)
160160
}
161+
enc, err := sdpEncryption(req.MediaEncryption)
162+
if err != nil {
163+
return nil, err
164+
}
161165
log = log.WithValues(
162166
"callID", req.SipCallId,
163167
"room", req.RoomName,
@@ -215,6 +219,7 @@ func (c *Client) createSIPParticipant(ctx context.Context, req *rpc.InternalCrea
215219
ringingTimeout: req.RingingTimeout.AsDuration(),
216220
maxCallDuration: req.MaxCallDuration.AsDuration(),
217221
enabledFeatures: req.EnabledFeatures,
222+
mediaEncryption: enc,
218223
}
219224
log.Infow("Creating SIP participant")
220225
call, err := c.newCall(ctx, c.conf, log, LocalTag(req.SipCallId), roomConf, sipConf, callInfo, ioClient)

pkg/sip/inbound.go

+38-14
Original file line numberDiff line numberDiff line change
@@ -412,17 +412,21 @@ func (c *inboundCall) handleInvite(ctx context.Context, req *sip.Request, trunkI
412412
pinPrompt = true
413413
}
414414

415-
// We need to start media first, otherwise we won't be able to send audio prompts to the caller, or receive DTMF.
416-
answerData, err := c.runMediaConn(req.Body(), conf, disp.EnabledFeatures)
417-
if err != nil {
418-
c.log.Errorw("Cannot start media", err)
419-
c.cc.RespondAndDrop(sip.StatusInternalServerError, "")
420-
c.close(true, callDropped, "media-failed")
421-
return err
415+
runMedia := func(enc livekit.SIPMediaEncryption) ([]byte, error) {
416+
answerData, err := c.runMediaConn(req.Body(), enc, conf, disp.EnabledFeatures)
417+
if err != nil {
418+
c.log.Errorw("Cannot start media", err)
419+
c.cc.RespondAndDrop(sip.StatusInternalServerError, "")
420+
c.close(true, callDropped, "media-failed")
421+
return nil, err
422+
}
423+
return answerData, nil
422424
}
423-
acceptCall := func() (bool, error) {
425+
426+
// We need to start media first, otherwise we won't be able to send audio prompts to the caller, or receive DTMF.
427+
acceptCall := func(answerData []byte) (bool, error) {
424428
c.log.Infow("Accepting the call", "headers", disp.Headers)
425-
if err = c.cc.Accept(ctx, answerData, disp.Headers); err != nil {
429+
if err := c.cc.Accept(ctx, answerData, disp.Headers); err != nil {
426430
c.log.Errorw("Cannot respond to INVITE", err)
427431
return false, err
428432
}
@@ -435,15 +439,30 @@ func (c *inboundCall) handleInvite(ctx context.Context, req *sip.Request, trunkI
435439
}
436440

437441
ok := false
442+
var answerData []byte
438443
if pinPrompt {
444+
var err error
439445
// Accept the call first on the SIP side, so that we can send audio prompts.
440-
if ok, err = acceptCall(); !ok {
446+
// This also means we have to pick encryption setting early, before room is selected.
447+
// Backend must explicitly enable encryption for pin prompts.
448+
answerData, err = runMedia(disp.MediaEncryption)
449+
if err != nil {
450+
return err // already sent a response
451+
}
452+
if ok, err = acceptCall(answerData); !ok {
441453
return err // could be success if the caller hung up
442454
}
443455
disp, ok, err = c.pinPrompt(ctx, trunkID)
444456
if !ok {
445457
return err // already sent a response. Could be success if user hung up
446458
}
459+
} else {
460+
// Start media with given encryption settings.
461+
var err error
462+
answerData, err = runMedia(disp.MediaEncryption)
463+
if err != nil {
464+
return err // already sent a response
465+
}
447466
}
448467
if len(disp.HeadersToAttributes) != 0 {
449468
p := &disp.Room.Participant
@@ -486,7 +505,7 @@ func (c *inboundCall) handleInvite(ctx context.Context, req *sip.Request, trunkI
486505
if ok, err := c.waitSubscribe(ctx, disp.RingingTimeout); !ok {
487506
return err // already sent a response. Could be success if caller hung up
488507
}
489-
if ok, err := acceptCall(); !ok {
508+
if ok, err := acceptCall(answerData); !ok {
490509
return err // already sent a response. Could be success if caller hung up
491510
}
492511
}
@@ -518,11 +537,16 @@ func (c *inboundCall) handleInvite(ctx context.Context, req *sip.Request, trunkI
518537
}
519538
}
520539

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

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

538-
answer, mconf, err := mp.SetOffer(offerData)
562+
answer, mconf, err := mp.SetOffer(offerData, e)
539563
if err != nil {
540564
return nil, err
541565
}

0 commit comments

Comments
 (0)