Skip to content

Commit

Permalink
feat(endpoint): nat & media_encryption attributes
Browse files Browse the repository at this point in the history
A new NAT parameter can be set on endpoints. It abstracts away the
necessary modifications to endpoint fields required for a far end NAT
phone to work with asterisk.

Support for setting media_encryption attributes on endpoints was also
added, however DTLS needs more configuration which so far has not been
implemented so in practical terms only sdes will work through eryth.

Closes #26.
Related to #23.
  • Loading branch information
crazybolillo committed Sep 30, 2024
1 parent 450b951 commit 251dad8
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 35 deletions.
12 changes: 12 additions & 0 deletions docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@ definitions:
type: string
displayName:
type: string
encryption:
type: string
extension:
type: string
id:
type: string
maxContacts:
type: integer
nat:
type: boolean
sid:
type: integer
transport:
Expand Down Expand Up @@ -74,12 +78,16 @@ definitions:
type: string
displayName:
type: string
encryption:
type: string
extension:
type: string
id:
type: string
maxContacts:
type: integer
nat:
type: boolean
password:
type: string
transport:
Expand All @@ -95,10 +103,14 @@ definitions:
type: string
displayName:
type: string
encryption:
type: string
extension:
type: string
maxContacts:
type: integer
nat:
type: boolean
password:
type: string
transport:
Expand Down
8 changes: 8 additions & 0 deletions internal/handler/endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ func MustCreate(handler http.Handler) func(*testing.T) {
Extension: "1234",
DisplayName: "Zinnia Elegans",
MaxContacts: 10,
Nat: true,
}
res := createEndpoint(t, handler, endpoint)
if res.Code != http.StatusCreated {
Expand All @@ -127,6 +128,7 @@ func MustCreate(handler http.Handler) func(*testing.T) {
Codecs: endpoint.Codecs,
MaxContacts: 10,
Extension: "1234",
Nat: true,
}

if !reflect.DeepEqual(got, want) {
Expand Down Expand Up @@ -198,15 +200,21 @@ func MustUpdate(handler http.Handler) func(*testing.T) {
Codecs: []string{"ulaw", "opus"},
Extension: "5061",
DisplayName: "Big Chungus",
Nat: false,
Encryption: "dtls",
}
res := createEndpoint(t, handler, endpoint)
want := parseEndpoint(t, res.Body)
want.MaxContacts = 5
want.Extension = "6072"
want.Nat = true
want.Encryption = "sdes"

res = updateEndpoint(t, handler, want.Sid, model.PatchedEndpoint{
MaxContacts: &want.MaxContacts,
Extension: &want.Extension,
Nat: &want.Nat,
Encryption: &want.Encryption,
})
if res.Code != http.StatusOK {
t.Errorf("invalid http code, got %d, want %d", res.Code, http.StatusOK)
Expand Down
6 changes: 6 additions & 0 deletions internal/model/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ type Endpoint struct {
Codecs []string `json:"codecs"`
MaxContacts int32 `json:"maxContacts"`
Extension string `json:"extension"`
Nat bool `json:"nat"`
Encryption string `json:"encryption"`
}

type NewEndpoint struct {
Expand All @@ -22,6 +24,8 @@ type NewEndpoint struct {
MaxContacts int32 `json:"maxContacts"`
Extension string `json:"extension"`
DisplayName string `json:"displayName"`
Nat bool `json:"nat"`
Encryption string `json:"encryption"`
}

type PatchedEndpoint struct {
Expand All @@ -32,6 +36,8 @@ type PatchedEndpoint struct {
Codecs []string `json:"codecs,"`
MaxContacts *int32 `json:"maxContacts,"`
Extension *string `json:"extension,"`
Nat *bool `json:"nat,"`
Encryption *string `json:"encryption,"`
}

type EndpointPageEntry struct {
Expand Down
56 changes: 50 additions & 6 deletions internal/service/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,22 @@ func displayNameFromClid(callerID string) string {
return callerID[1:end]
}

func parseMediaEncryption(value string) (sqlc.NullPjsipMediaEncryptionValues, error) {
switch value {
case "sdes":
fallthrough
case "dtls":
return sqlc.NullPjsipMediaEncryptionValues{
PjsipMediaEncryptionValues: sqlc.PjsipMediaEncryptionValues(value),
Valid: true,
}, nil
case "":
return sqlc.NullPjsipMediaEncryptionValues{}, nil
default:
return sqlc.NullPjsipMediaEncryptionValues{}, fmt.Errorf("invalid value for media_encryption '%s'", value)
}
}

func (e *EndpointService) Create(ctx context.Context, payload model.NewEndpoint) (model.Endpoint, error) {
tx, err := e.Begin(ctx)
if err != nil {
Expand All @@ -62,13 +78,25 @@ func (e *EndpointService) Create(ctx context.Context, payload model.NewEndpoint)
return model.Endpoint{}, err
}

natValue := sqlc.NullAstBoolValues{AstBoolValues: "no", Valid: true}
if payload.Nat {
natValue.AstBoolValues = "yes"
}

mediaValue, err := parseMediaEncryption(payload.Encryption)
if err != nil {
return model.Endpoint{}, err
}

sid, err := queries.NewEndpoint(ctx, sqlc.NewEndpointParams{
ID: payload.ID,
Accountcode: db.Text(cmp.Or(payload.AccountCode, payload.ID)),
Transport: db.Text(payload.Transport),
Context: db.Text(payload.Context),
Allow: db.Text(strings.Join(payload.Codecs, ",")),
Callerid: db.Text(fmt.Sprintf(`"%s" <%s>`, payload.DisplayName, payload.ID)),
ID: payload.ID,
Accountcode: db.Text(cmp.Or(payload.AccountCode, payload.ID)),
Transport: db.Text(payload.Transport),
Context: db.Text(payload.Context),
Allow: db.Text(strings.Join(payload.Codecs, ",")),
Callerid: db.Text(fmt.Sprintf(`"%s" <%s>`, payload.DisplayName, payload.ID)),
Nat: natValue,
MediaEncryption: mediaValue,
})
if err != nil {
return model.Endpoint{}, err
Expand Down Expand Up @@ -117,6 +145,8 @@ func (e *EndpointService) Read(ctx context.Context, sid int32) (model.Endpoint,
Codecs: strings.Split(row.Allow.String, ","),
MaxContacts: row.MaxContacts.Int32,
Extension: row.Extension.String,
Nat: row.Nat.Bool,
Encryption: string(row.MediaEncryption.PjsipMediaEncryptionValues),
}
return endpoint, nil
}
Expand Down Expand Up @@ -159,6 +189,20 @@ func (e *EndpointService) Update(ctx context.Context, sid int32, payload model.P
} else {
patchedEndpoint.Allow = endpoint.Allow
}
if payload.Nat != nil {
patchedEndpoint.Nat = sqlc.NullAstBoolValues{AstBoolValues: "yes", Valid: *payload.Nat}
} else {
patchedEndpoint.Nat = sqlc.NullAstBoolValues{AstBoolValues: "yes", Valid: endpoint.Nat.Bool}
}
if payload.Encryption != nil {
patchedEndpoint.MediaEncryption, err = parseMediaEncryption(*payload.Encryption)
if err != nil {
return model.Endpoint{}, err
}
} else {
patchedEndpoint.MediaEncryption = endpoint.MediaEncryption
}

err = queries.UpdateEndpointBySid(ctx, patchedEndpoint)
if err != nil {
return model.Endpoint{}, err
Expand Down
87 changes: 63 additions & 24 deletions internal/sqlc/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 251dad8

Please sign in to comment.