mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-10-03 17:53:59 +00:00
[mod_sofia] Pass deflect targets with a URI scheme through unchanged
The deflect handler prefixed `sip:` and appended the profile address to any
target not starting with `sip:`. `tel:`, `sips:` and other URIs went out as
`sip:tel:...@<ip>`. A name-addr target, as a proxied `Refer-To` or
`${sip_refer_to}` passes it, went out as `sip:<sip:...>@<ip>`, or was not sent
at all when it carried a display name, URI params or `?Replaces=`.
`sofia_deflect_build_refer_to()` now builds the `Refer-To`:
- a target that has a URI scheme or contains `<` goes out as given
- `user@host`, with or without a host port, only gains the `sip:` scheme
- a bare user or number is addressed at the profile, as before
- an empty or `NULL` target, which the `deflect` app passes when run without
data, is rejected; the handler logs it and sends no REFER
Adds `test_sofia_deflect_build_refer_to` to `test_sofia_funcs`: rejected
targets, bare users with IPv4 and IPv6 profile addresses, `user@host` forms,
URIs with a scheme, name-addr forms, the known limitations and truncation.
This commit is contained in:
@@ -1352,6 +1352,53 @@ static switch_status_t sofia_send_dtmf(switch_core_session_t *session, const swi
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* True when the deflect target already carries a URI scheme or is a name-addr */
|
||||
static switch_bool_t sofia_deflect_target_is_uri(const char *target)
|
||||
{
|
||||
const char *p = target;
|
||||
|
||||
if (strchr(target, '<')) {
|
||||
return SWITCH_TRUE;
|
||||
}
|
||||
|
||||
/* A scheme is a letter followed by letters, digits, '+', '-' or '.', terminated by ':' */
|
||||
if (!isalpha((unsigned char)*p)) {
|
||||
return SWITCH_FALSE;
|
||||
}
|
||||
|
||||
for (p++; *p; p++) {
|
||||
if (*p == ':') {
|
||||
return SWITCH_TRUE;
|
||||
}
|
||||
|
||||
if (!isalnum((unsigned char)*p) && *p != '+' && *p != '-' && *p != '.') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SWITCH_FALSE;
|
||||
}
|
||||
|
||||
/* Builds the Refer-To for a deflect target; fails for an empty target */
|
||||
switch_status_t sofia_deflect_build_refer_to(const char *target, const char *sipip, char *buf, switch_size_t buflen)
|
||||
{
|
||||
if (zstr(target)) {
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
if (sofia_deflect_target_is_uri(target)) {
|
||||
switch_copy_string(buf, target, buflen);
|
||||
} else if (strchr(target, '@')) {
|
||||
switch_snprintf(buf, buflen, "sip:%s", target);
|
||||
} else {
|
||||
const char *format = strchr(sipip, ':') ? "sip:%s@[%s]" : "sip:%s@%s";
|
||||
|
||||
switch_snprintf(buf, buflen, format, target, sipip);
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status_t sofia_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg)
|
||||
{
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
@@ -1563,19 +1610,20 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
|
||||
|
||||
case SWITCH_MESSAGE_INDICATE_DEFLECT: {
|
||||
|
||||
char *extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_HEADER_PREFIX);
|
||||
char *extra_headers;
|
||||
char ref_to[1024] = "";
|
||||
const char *var;
|
||||
const char *session_id_header = sofia_glue_session_id_header(session, tech_pvt->profile);
|
||||
const char *session_id_header;
|
||||
|
||||
if (strncasecmp(msg->string_arg, "sip:", 4)) {
|
||||
const char *format = strchr(tech_pvt->profile->sipip, ':') ? "sip:%s@[%s]" : "sip:%s@%s";
|
||||
|
||||
switch_snprintf(ref_to, sizeof(ref_to), format, msg->string_arg, tech_pvt->profile->sipip);
|
||||
} else {
|
||||
switch_set_string(ref_to, msg->string_arg);
|
||||
if (sofia_deflect_build_refer_to(msg->string_arg, tech_pvt->profile->sipip, ref_to, sizeof(ref_to)) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Deflect requires a target\n");
|
||||
msg->string_reply = "no target";
|
||||
break;
|
||||
}
|
||||
|
||||
extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_HEADER_PREFIX);
|
||||
session_id_header = sofia_glue_session_id_header(session, tech_pvt->profile);
|
||||
|
||||
nua_refer(tech_pvt->nh, SIPTAG_REFER_TO_STR(ref_to), SIPTAG_REFERRED_BY_STR(tech_pvt->contact_url),
|
||||
TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)),
|
||||
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <test/switch_test.h>
|
||||
|
||||
int protect_dest_uri(switch_caller_profile_t *cp);
|
||||
switch_status_t sofia_deflect_build_refer_to(const char *target, const char *sipip, char *buf, switch_size_t buflen);
|
||||
|
||||
static int timeout_sec = 10;
|
||||
static switch_interval_time_t delay_start_ms = 5000;
|
||||
@@ -122,6 +123,77 @@ FST_TEST_BEGIN(originate_test)
|
||||
}
|
||||
FST_TEST_END()
|
||||
|
||||
FST_TEST_BEGIN(test_sofia_deflect_build_refer_to)
|
||||
{
|
||||
char ref_to[1024];
|
||||
char short_buf[12];
|
||||
size_t i;
|
||||
const char *sipip = "203.0.113.5";
|
||||
/* expect NULL means the target is rejected */
|
||||
struct {
|
||||
const char *target;
|
||||
const char *expect;
|
||||
} cases[] = {
|
||||
{ NULL, NULL },
|
||||
{ "", NULL },
|
||||
/* a bare user or number is addressed at the profile */
|
||||
{ "1234", switch_core_sprintf(fst_pool, "sip:1234@%s", sipip) },
|
||||
{ "+15551234567", switch_core_sprintf(fst_pool, "sip:+15551234567@%s", sipip) },
|
||||
/* user@host only gains the scheme, including a host port or an IPv6 literal */
|
||||
{ "bob@example.com", "sip:bob@example.com" },
|
||||
{ "bob@198.51.100.7:2222", "sip:bob@198.51.100.7:2222" },
|
||||
{ "bob@[2001:db8::7]", "sip:bob@[2001:db8::7]" },
|
||||
/* a URI with a scheme goes out as given */
|
||||
{ "sip:bob@example.com", "sip:bob@example.com" },
|
||||
{ "sips:bob@example.com", "sips:bob@example.com" },
|
||||
{ "SIPS:bob@example.com", "SIPS:bob@example.com" },
|
||||
{ "sip:bob@198.51.100.7:2222", "sip:bob@198.51.100.7:2222" },
|
||||
{ "tel:+15551234567", "tel:+15551234567" },
|
||||
{ "urn:service:sos", "urn:service:sos" },
|
||||
/* a name-addr goes out as given */
|
||||
{ "<sip:bob@example.com>", "<sip:bob@example.com>" },
|
||||
{ "<sip:bob@example.com;user=phone>", "<sip:bob@example.com;user=phone>" },
|
||||
{ "\"Bob\" <sip:bob@example.com>", "\"Bob\" <sip:bob@example.com>" },
|
||||
{ "<sip:bob@example.com?Replaces=xfer-42%3Bto-tag%3Dt1%3Bfrom-tag%3Df1>", "<sip:bob@example.com?Replaces=xfer-42%3Bto-tag%3Dt1%3Bfrom-tag%3Df1>" },
|
||||
{ "<sip:bob@example.com>;x-leg=b", "<sip:bob@example.com>;x-leg=b" },
|
||||
/* Known limitations: a host:port without a user, or a user:password, reads as a scheme or is
|
||||
* addressed at the profile, and none of these is a valid Refer-To */
|
||||
{ "pbx.example.com:5060", "pbx.example.com:5060" },
|
||||
{ "198.51.100.7:5060", switch_core_sprintf(fst_pool, "sip:198.51.100.7:5060@%s", sipip) },
|
||||
{ "bob:secret@example.com", "bob:secret@example.com" },
|
||||
};
|
||||
|
||||
for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
|
||||
switch_status_t status;
|
||||
|
||||
ref_to[0] = '\0';
|
||||
status = sofia_deflect_build_refer_to(cases[i].target, sipip, ref_to, sizeof(ref_to));
|
||||
|
||||
if (!cases[i].expect) {
|
||||
fst_xcheck(status == SWITCH_STATUS_FALSE, "an empty deflect target must be rejected");
|
||||
continue;
|
||||
}
|
||||
|
||||
fst_xcheck(status == SWITCH_STATUS_SUCCESS, cases[i].target);
|
||||
fst_check_string_equals(ref_to, cases[i].expect);
|
||||
}
|
||||
|
||||
/* An IPv6 profile address is bracketed */
|
||||
sofia_deflect_build_refer_to("1234", "2001:db8:cafe::5", ref_to, sizeof(ref_to));
|
||||
fst_check_string_equals(ref_to, "sip:1234@[2001:db8:cafe::5]");
|
||||
|
||||
/* The result is truncated to buflen on every branch */
|
||||
sofia_deflect_build_refer_to("1234567", sipip, short_buf, sizeof(short_buf));
|
||||
fst_check_string_equals(short_buf, "sip:1234567");
|
||||
|
||||
sofia_deflect_build_refer_to("bob@example.com", sipip, short_buf, sizeof(short_buf));
|
||||
fst_check_string_equals(short_buf, "sip:bob@exa");
|
||||
|
||||
sofia_deflect_build_refer_to("sip:bob@example.com", sipip, short_buf, sizeof(short_buf));
|
||||
fst_check_string_equals(short_buf, "sip:bob@exa");
|
||||
}
|
||||
FST_TEST_END()
|
||||
|
||||
#if HAVE_STIRSHAKEN
|
||||
FST_TEST_BEGIN(sofia_verify_identity_test_no_identity)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user