Skip to content

Commit 9518af0

Browse files
authored
Support SIP media encryption. (#499)
1 parent 7a30877 commit 9518af0

File tree

2 files changed

+105
-11
lines changed

2 files changed

+105
-11
lines changed

cmd/lk/proto.go

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ import (
2121
"os"
2222
"reflect"
2323

24-
"github.com/livekit/livekit-cli/pkg/util"
2524
"github.com/urfave/cli/v3"
2625
"google.golang.org/protobuf/encoding/protojson"
2726
"google.golang.org/protobuf/proto"
27+
28+
"github.com/livekit/livekit-cli/pkg/util"
2829
)
2930

3031
const flagRequest = "request"
@@ -98,20 +99,36 @@ func RequestDesc[T any, _ protoType[T]]() string {
9899
return typ + " as JSON file"
99100
}
100101

101-
func createAndPrint[T any, P protoTypeValidator[T], R any](
102+
func createAndPrintFile[T any, P protoTypeValidator[T], R any](
102103
ctx context.Context,
103104
cmd *cli.Command, file string,
105+
fill func(p P) error,
104106
create func(ctx context.Context, p P) (R, error),
105107
print func(r R),
106108
) error {
107109
req, err := ReadRequestFileOrLiteral[T, P](file)
108110
if err != nil {
109111
return fmt.Errorf("could not read request: %w", err)
110112
}
113+
return createAndPrintReq(ctx, cmd, req, fill, create, print)
114+
}
115+
116+
func createAndPrintReq[T any, P protoTypeValidator[T], R any](
117+
ctx context.Context,
118+
cmd *cli.Command, req P,
119+
fill func(p P) error,
120+
create func(ctx context.Context, p P) (R, error),
121+
print func(r R),
122+
) error {
123+
if fill != nil {
124+
if err := fill(req); err != nil {
125+
return err
126+
}
127+
}
111128
if cmd.Bool("verbose") {
112129
util.PrintJSON(req)
113130
}
114-
if err = req.Validate(); err != nil {
131+
if err := req.Validate(); err != nil {
115132
return err
116133
}
117134
info, err := create(ctx, req)
@@ -146,15 +163,20 @@ func createAndPrintLegacy[T any, P protoType[T], R any](
146163
func createAndPrintReqs[T any, P protoTypeValidator[T], R any](
147164
ctx context.Context,
148165
cmd *cli.Command,
166+
fill func(p P) error,
149167
create func(ctx context.Context, p P) (R, error),
150168
print func(r R),
151169
) error {
152170
args := cmd.Args()
153171
if !args.Present() {
154-
return errors.New("at least one JSON request file is required")
172+
if fill == nil {
173+
return errors.New("at least one JSON request file is required")
174+
}
175+
var req P = new(T)
176+
return createAndPrintReq(ctx, cmd, req, fill, create, print)
155177
}
156178
for _, file := range args.Slice() {
157-
if err := createAndPrint(ctx, cmd, file, create, print); err != nil {
179+
if err := createAndPrintFile(ctx, cmd, file, fill, create, print); err != nil {
158180
return err
159181
}
160182
}

cmd/lk/sip.go

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ package main
1717
import (
1818
"context"
1919
"fmt"
20+
"maps"
21+
"slices"
2022
"strconv"
2123
"strings"
2224
"time"
@@ -118,6 +120,24 @@ var (
118120
Usage: "Create a SIP Participant",
119121
Action: createSIPParticipant,
120122
ArgsUsage: RequestDesc[livekit.CreateSIPParticipantRequest](),
123+
Flags: []cli.Flag{
124+
&cli.StringFlag{
125+
Name: "trunk",
126+
Usage: "`SIP_TRUNK_ID` to use for the call (overrides json config)",
127+
},
128+
&cli.StringFlag{
129+
Name: "number",
130+
Usage: "`SIP_NUMBER` to use for the call (overrides json config)",
131+
},
132+
&cli.StringFlag{
133+
Name: "call",
134+
Usage: "`SIP_CALL_TO` number to use (overrides json config)",
135+
},
136+
&cli.StringFlag{
137+
Name: "room",
138+
Usage: "`ROOM_NAME` to place the call to (overrides json config)",
139+
},
140+
},
121141
},
122142
{
123143
Name: "transfer",
@@ -215,15 +235,15 @@ func createSIPInboundTrunk(ctx context.Context, cmd *cli.Command) error {
215235
if err != nil {
216236
return err
217237
}
218-
return createAndPrintReqs(ctx, cmd, cli.CreateSIPInboundTrunk, printSIPInboundTrunkID)
238+
return createAndPrintReqs(ctx, cmd, nil, cli.CreateSIPInboundTrunk, printSIPInboundTrunkID)
219239
}
220240

221241
func createSIPOutboundTrunk(ctx context.Context, cmd *cli.Command) error {
222242
cli, err := createSIPClient(cmd)
223243
if err != nil {
224244
return err
225245
}
226-
return createAndPrintReqs(ctx, cmd, cli.CreateSIPOutboundTrunk, printSIPOutboundTrunkID)
246+
return createAndPrintReqs(ctx, cmd, nil, cli.CreateSIPOutboundTrunk, printSIPOutboundTrunkID)
227247
}
228248

229249
func userPass(user string, hasPass bool) string {
@@ -237,6 +257,40 @@ func userPass(user string, hasPass bool) string {
237257
return user + " / " + passStr
238258
}
239259

260+
func printHeaders(m map[string]string) string {
261+
if len(m) == 0 {
262+
return ""
263+
}
264+
keys := slices.Collect(maps.Keys(m))
265+
slices.Sort(keys)
266+
var buf strings.Builder
267+
for i, key := range keys {
268+
if i != 0 {
269+
buf.WriteString("\n")
270+
}
271+
v := m[key]
272+
buf.WriteString(key)
273+
buf.WriteString("=")
274+
buf.WriteString(v)
275+
}
276+
return buf.String()
277+
}
278+
279+
func printHeaderMaps(arr ...map[string]string) string {
280+
var out []string
281+
for _, m := range arr {
282+
s := printHeaders(m)
283+
if s == "" {
284+
continue
285+
}
286+
out = append(out, s)
287+
}
288+
if len(out) == 0 {
289+
return ""
290+
}
291+
return strings.Join(out, "\n\n")
292+
}
293+
240294
func listSipTrunk(ctx context.Context, cmd *cli.Command) error {
241295
cli, err := createSIPClient(cmd)
242296
if err != nil {
@@ -271,14 +325,16 @@ func listSipInboundTrunk(ctx context.Context, cmd *cli.Command) error {
271325
"SipTrunkID", "Name", "Numbers",
272326
"AllowedAddresses", "AllowedNumbers",
273327
"Authentication",
328+
"Encryption",
274329
"Headers",
275330
"Metadata",
276331
}, func(item *livekit.SIPInboundTrunkInfo) []string {
277332
return []string{
278333
item.SipTrunkId, item.Name, strings.Join(item.Numbers, ","),
279334
strings.Join(item.AllowedAddresses, ","), strings.Join(item.AllowedNumbers, ","),
280335
userPass(item.AuthUsername, item.AuthPassword != ""),
281-
fmt.Sprintf("%v, %v", item.Headers, item.HeadersToAttributes),
336+
strings.TrimPrefix(item.MediaEncryption.String(), "SIP_MEDIA_ENCRYPT_"),
337+
printHeaderMaps(item.Headers, item.HeadersToAttributes),
282338
item.Metadata,
283339
}
284340
})
@@ -294,6 +350,7 @@ func listSipOutboundTrunk(ctx context.Context, cmd *cli.Command) error {
294350
"Address", "Transport",
295351
"Numbers",
296352
"Authentication",
353+
"Encryption",
297354
"Headers",
298355
"Metadata",
299356
}, func(item *livekit.SIPOutboundTrunkInfo) []string {
@@ -302,7 +359,8 @@ func listSipOutboundTrunk(ctx context.Context, cmd *cli.Command) error {
302359
item.Address, strings.TrimPrefix(item.Transport.String(), "SIP_TRANSPORT_"),
303360
strings.Join(item.Numbers, ","),
304361
userPass(item.AuthUsername, item.AuthPassword != ""),
305-
fmt.Sprintf("%v, %v", item.Headers, item.HeadersToAttributes),
362+
strings.TrimPrefix(item.MediaEncryption.String(), "SIP_MEDIA_ENCRYPT_"),
363+
printHeaderMaps(item.Headers, item.HeadersToAttributes),
306364
item.Metadata,
307365
}
308366
})
@@ -357,7 +415,7 @@ func createSIPDispatchRule(ctx context.Context, cmd *cli.Command) error {
357415
if err != nil {
358416
return err
359417
}
360-
return createAndPrintReqs(ctx, cmd, cli.CreateSIPDispatchRule, printSIPDispatchRuleID)
418+
return createAndPrintReqs(ctx, cmd, nil, cli.CreateSIPDispatchRule, printSIPDispatchRuleID)
361419
}
362420

363421
func createSIPDispatchRuleLegacy(ctx context.Context, cmd *cli.Command) error {
@@ -447,7 +505,21 @@ func createSIPParticipant(ctx context.Context, cmd *cli.Command) error {
447505
if err != nil {
448506
return err
449507
}
450-
return createAndPrintReqs(ctx, cmd, func(ctx context.Context, req *livekit.CreateSIPParticipantRequest) (*livekit.SIPParticipantInfo, error) {
508+
return createAndPrintReqs(ctx, cmd, func(req *livekit.CreateSIPParticipantRequest) error {
509+
if v := cmd.String("trunk"); v != "" {
510+
req.SipTrunkId = v
511+
}
512+
if v := cmd.String("number"); v != "" {
513+
req.SipNumber = v
514+
}
515+
if v := cmd.String("call"); v != "" {
516+
req.SipCallTo = v
517+
}
518+
if v := cmd.String("room"); v != "" {
519+
req.RoomName = v
520+
}
521+
return req.Validate()
522+
}, func(ctx context.Context, req *livekit.CreateSIPParticipantRequest) (*livekit.SIPParticipantInfo, error) {
451523
// CreateSIPParticipant will wait for LiveKit Participant to be created and that can take some time.
452524
// Default deadline is too short, thus, we must set a higher deadline for it.
453525
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)

0 commit comments

Comments
 (0)