Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ prometheus_port: port used to collect prometheus metrics. Used for autoscaling
log_level: debug, info, warn, or error (default info)
sip_port: port to listen and send SIP traffic (default 5060)
rtp_port: port to listen and send RTP traffic (default 10000-20000)
allow_custom_from_hostname: if true, allows the From header to be set to a custom hostname retrieved from the outbound trunk (default false)
```

The config file can be added to a mounted volume with its location passed in the SIP_CONFIG_FILE env var, or its body can be passed in the SIP_CONFIG_BODY env var.
Expand Down
3 changes: 3 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ type Config struct {
// InboundWaitACK forces SIP to wait for an ACK to 200 OK before proceeding with the call.
InboundWaitACK bool `yaml:"inbound_wait_ack"`
} `yaml:"experimental"`

// AllowCustomFromHostname allows the FROM header to be set to a custom hostname.
AllowCustomFromHostname bool `yaml:"allow_custom_from_hostname"`
}

func NewConfig(confString string) (*Config, error) {
Expand Down
3 changes: 3 additions & 0 deletions pkg/sip/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@
}

func (c *Client) createSIPParticipant(ctx context.Context, req *rpc.InternalCreateSIPParticipantRequest) (resp *rpc.InternalCreateSIPParticipantResponse, retErr error) {
if c.conf.AllowCustomFromHostname {
req.Hostname = req.FromHostname

Check failure on line 176 in pkg/sip/client.go

View workflow job for this annotation

GitHub Actions / test

req.FromHostname undefined (type *rpc.InternalCreateSIPParticipantRequest has no field or method FromHostname)

Check failure on line 176 in pkg/sip/client.go

View workflow job for this annotation

GitHub Actions / test

req.FromHostname undefined (type *rpc.InternalCreateSIPParticipantRequest has no field or method FromHostname) (compile)
}
if c.mon.Health() != stats.HealthOK {
return nil, siperrors.ErrUnavailable
}
Expand Down
82 changes: 82 additions & 0 deletions pkg/sip/outbound_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,85 @@

cancel()
}
func TestCreateSIPParticipant_CustomFromHostname(t *testing.T) {
t.Run("when allowCustomFromHostname is false", func(t *testing.T) {
customFromHost := "custom-from.example.com"

client := NewOutboundTestClient(t, TestClientConfig{})
client.conf.AllowCustomFromHostname = false

req := MinimalCreateSIPParticipantRequest()
req.FromHostname = customFromHost

Check failure on line 134 in pkg/sip/outbound_test.go

View workflow job for this annotation

GitHub Actions / test

req.FromHostname undefined (type *rpc.InternalCreateSIPParticipantRequest has no field or method FromHostname)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
_, _ = client.CreateSIPParticipant(ctx, req)
}()

var sipClient *testSIPClient
select {
case sipClient = <-createdClients:
t.Cleanup(func() { _ = sipClient.Close() })
case <-time.After(100 * time.Millisecond):
require.Fail(t, "expected client to be created")
return
}

var tr *transactionRequest
select {
case tr = <-sipClient.transactions:
t.Cleanup(func() { tr.transaction.Terminate() })
case <-time.After(500 * time.Millisecond):
require.Fail(t, "expected transaction request to be created")
return
}

require.NotNil(t, tr)
require.NotNil(t, tr.req)
require.NotNil(t, tr.req.From())
require.NotEqual(t, customFromHost, tr.req.From().Address.Host)

require.NoError(t, tr.transaction.SendResponse(sip.NewResponseFromRequest(tr.req, sip.StatusBusyHere, "Busy Here", nil)))
})

t.Run("when allowCustomFromHostname is true", func(t *testing.T) {
customFromHost := "custom-from.example.com"

client := NewOutboundTestClient(t, TestClientConfig{})
client.conf.AllowCustomFromHostname = true

req := MinimalCreateSIPParticipantRequest()
req.FromHostname = customFromHost

Check failure on line 175 in pkg/sip/outbound_test.go

View workflow job for this annotation

GitHub Actions / test

req.FromHostname undefined (type *rpc.InternalCreateSIPParticipantRequest has no field or method FromHostname) (compile)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
_, _ = client.CreateSIPParticipant(ctx, req)
}()

var sipClient *testSIPClient
select {
case sipClient = <-createdClients:
t.Cleanup(func() { _ = sipClient.Close() })
case <-time.After(100 * time.Millisecond):
require.Fail(t, "expected client to be created")
return
}

var tr *transactionRequest
select {
case tr = <-sipClient.transactions:
t.Cleanup(func() { tr.transaction.Terminate() })
case <-time.After(500 * time.Millisecond):
require.Fail(t, "expected transaction request to be created")
return
}

require.NotNil(t, tr)
require.NotNil(t, tr.req)
require.NotNil(t, tr.req.From())
require.Equal(t, customFromHost, tr.req.From().Address.Host)
require.NoError(t, tr.transaction.SendResponse(sip.NewResponseFromRequest(tr.req, sip.StatusBusyHere, "Busy Here", nil)))
})
}
Loading