Skip to content

Commit 70d086e

Browse files
committed
feat: Add allow_custom_from_hostname config option to control custom From header hostname livekit/protocol#1419
1 parent ded5856 commit 70d086e

4 files changed

Lines changed: 89 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ prometheus_port: port used to collect prometheus metrics. Used for autoscaling
6262
log_level: debug, info, warn, or error (default info)
6363
sip_port: port to listen and send SIP traffic (default 5060)
6464
rtp_port: port to listen and send RTP traffic (default 10000-20000)
65+
allow_custom_from_hostname: if true, allows the From header to be set to a custom hostname retrieved from the outbound trunk (default false)
6566
```
6667
6768
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.

pkg/config/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ type Config struct {
124124
// InboundWaitACK forces SIP to wait for an ACK to 200 OK before proceeding with the call.
125125
InboundWaitACK bool `yaml:"inbound_wait_ack"`
126126
} `yaml:"experimental"`
127+
128+
// AllowCustomFromHostname allows the FROM header to be set to a custom hostname.
129+
AllowCustomFromHostname bool `yaml:"allow_custom_from_hostname"`
127130
}
128131

129132
func NewConfig(confString string) (*Config, error) {

pkg/sip/client.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ func (c *Client) CreateSIPParticipant(ctx context.Context, req *rpc.InternalCrea
172172
}
173173

174174
func (c *Client) createSIPParticipant(ctx context.Context, req *rpc.InternalCreateSIPParticipantRequest) (resp *rpc.InternalCreateSIPParticipantResponse, retErr error) {
175+
if c.conf.AllowCustomFromHostname {
176+
req.Hostname = req.FromHostname
177+
}
175178
if c.mon.Health() != stats.HealthOK {
176179
return nil, siperrors.ErrUnavailable
177180
}

pkg/sip/outbound_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,85 @@ func TestOutboundRouteHeaderWithRecordRoute(t *testing.T) {
123123

124124
cancel()
125125
}
126+
func TestCreateSIPParticipant_CustomFromHostname(t *testing.T) {
127+
t.Run("when allowCustomFromHostname is false", func(t *testing.T) {
128+
customFromHost := "custom-from.example.com"
129+
130+
client := NewOutboundTestClient(t, TestClientConfig{})
131+
client.conf.AllowCustomFromHostname = false
132+
133+
req := MinimalCreateSIPParticipantRequest()
134+
req.FromHostname = customFromHost
135+
136+
ctx, cancel := context.WithCancel(context.Background())
137+
defer cancel()
138+
go func() {
139+
_, _ = client.CreateSIPParticipant(ctx, req)
140+
}()
141+
142+
var sipClient *testSIPClient
143+
select {
144+
case sipClient = <-createdClients:
145+
t.Cleanup(func() { _ = sipClient.Close() })
146+
case <-time.After(100 * time.Millisecond):
147+
require.Fail(t, "expected client to be created")
148+
return
149+
}
150+
151+
var tr *transactionRequest
152+
select {
153+
case tr = <-sipClient.transactions:
154+
t.Cleanup(func() { tr.transaction.Terminate() })
155+
case <-time.After(500 * time.Millisecond):
156+
require.Fail(t, "expected transaction request to be created")
157+
return
158+
}
159+
160+
require.NotNil(t, tr)
161+
require.NotNil(t, tr.req)
162+
require.NotNil(t, tr.req.From())
163+
require.NotEqual(t, customFromHost, tr.req.From().Address.Host)
164+
165+
require.NoError(t, tr.transaction.SendResponse(sip.NewResponseFromRequest(tr.req, sip.StatusBusyHere, "Busy Here", nil)))
166+
})
167+
168+
t.Run("when allowCustomFromHostname is true", func(t *testing.T) {
169+
customFromHost := "custom-from.example.com"
170+
171+
client := NewOutboundTestClient(t, TestClientConfig{})
172+
client.conf.AllowCustomFromHostname = true
173+
174+
req := MinimalCreateSIPParticipantRequest()
175+
req.FromHostname = customFromHost
176+
177+
ctx, cancel := context.WithCancel(context.Background())
178+
defer cancel()
179+
go func() {
180+
_, _ = client.CreateSIPParticipant(ctx, req)
181+
}()
182+
183+
var sipClient *testSIPClient
184+
select {
185+
case sipClient = <-createdClients:
186+
t.Cleanup(func() { _ = sipClient.Close() })
187+
case <-time.After(100 * time.Millisecond):
188+
require.Fail(t, "expected client to be created")
189+
return
190+
}
191+
192+
var tr *transactionRequest
193+
select {
194+
case tr = <-sipClient.transactions:
195+
t.Cleanup(func() { tr.transaction.Terminate() })
196+
case <-time.After(500 * time.Millisecond):
197+
require.Fail(t, "expected transaction request to be created")
198+
return
199+
}
200+
201+
require.NotNil(t, tr)
202+
require.NotNil(t, tr.req)
203+
require.NotNil(t, tr.req.From())
204+
require.Equal(t, customFromHost, tr.req.From().Address.Host)
205+
require.NoError(t, tr.transaction.SendResponse(sip.NewResponseFromRequest(tr.req, sip.StatusBusyHere, "Busy Here", nil)))
206+
})
207+
}

0 commit comments

Comments
 (0)