Merge branch 'master' into v1.11

This commit is contained in:
Andrey Volk
2026-05-26 23:41:26 +03:00
49 changed files with 1903 additions and 262 deletions
+4 -4
View File
@@ -35,11 +35,11 @@ jobs:
autoconf \
automake \
curl \
ffmpeg@5 \
ffmpeg@7 \
gnu-sed \
jpeg \
ldns \
libpq@16 \
libpq@18 \
libsndfile \
libtool \
lua \
@@ -60,8 +60,8 @@ jobs:
signalwire/homebrew-signalwire/spandsp \
&& \
brew link --force --overwrite \
ffmpeg@5 \
libpq@16
ffmpeg@7 \
libpq@18
- name: Bootstrap FreeSWITCH
run: ./bootstrap.sh -j
+7
View File
@@ -107,6 +107,13 @@ jobs:
run: |
./run-tests.sh ${{ inputs.total-groups }} ${{ inputs.current-group }} --output-dir logs || exit 1
- name: Run libesl tests
if: ${{ inputs.current-group == 1 }}
shell: bash
working-directory: ${{ inputs.working-directory }}/../../libs/esl
run: |
make check
- name: Collect unit test logs
if: always()
shell: bash
+1 -1
View File
@@ -1 +1 @@
1.11.0-release
1.11.1-release
+7
View File
@@ -430,4 +430,11 @@
<X-PRE-PROCESS cmd="set" data="video_mute_png=$${images_dir}/default-mute.png"/>
<X-PRE-PROCESS cmd="set" data="video_no_avatar_png=$${images_dir}/default-avatar.png"/>
<!-- SIP 603+ call blocking passthrough control.
true: Forward "Network Blocked" phrase and Reason header to the caller.
false: Strip Reason header, send clean 603 Decline.
unset: Default behavior (Reason passes through, phrase reset to "Decline").
Works independently of disable_q850_reason for selective 603+ forwarding. -->
<!--<Z-PRE-PROCESS cmd="set" data="sip_603plus_passthrough=true"/>-->
</include>
+9 -3
View File
@@ -3,10 +3,10 @@
# Must change all of the below together
# For a release, set revision for that tagged release as well and uncomment
AC_INIT([freeswitch], [1.11.0-release], bugs@freeswitch.org)
AC_INIT([freeswitch], [1.11.1-release], bugs@freeswitch.org)
AC_SUBST(SWITCH_VERSION_MAJOR, [1])
AC_SUBST(SWITCH_VERSION_MINOR, [11])
AC_SUBST(SWITCH_VERSION_MICRO, [0-release])
AC_SUBST(SWITCH_VERSION_MICRO, [1-release])
AC_SUBST(SWITCH_VERSION_REVISION, [])
AC_SUBST(SWITCH_VERSION_REVISION_HUMAN, [])
@@ -308,6 +308,11 @@ SWITCH_AM_CXXFLAGS="-I${switch_srcdir}/src/include -I${switch_builddir}/src/incl
SWITCH_AM_CPPFLAGS="-I${switch_srcdir}/src/include -I${switch_builddir}/src/include -I${switch_srcdir}/libs/libteletone/src"
SWITCH_AM_LDFLAGS="-lm"
# Cap cJSON parser recursion depth. Default upstream limit (1000) can overflow
# small thread stacks; both vendored cJSON copies (src/ and libs/esl/) honor this.
APR_ADDTO(SWITCH_AM_CFLAGS, [-DCJSON_NESTING_LIMIT=64])
APR_ADDTO(SWITCH_AM_CXXFLAGS, [-DCJSON_NESTING_LIMIT=64])
#set SOLINK variable based on compiler and host
if test "x${ax_cv_c_compiler_vendor}" = "xsun" ; then
SOLINK="-Bdynamic -dy -G"
@@ -1518,7 +1523,7 @@ PKG_CHECK_MODULES([V8FS_STATIC], [v8-6.1_static >= 6.1.298],[
])
])
PKG_CHECK_MODULES([KS], [libks2 >= 2.0.0],[
PKG_CHECK_MODULES([KS], [libks2 >= 2.0.11],[
AM_CONDITIONAL([HAVE_KS],[true])],[
PKG_CHECK_MODULES([KS], [libks >= 1.8.2],[
AM_CONDITIONAL([HAVE_KS],[true])],[
@@ -2140,6 +2145,7 @@ AC_CONFIG_FILES([Makefile
build/standalone_module/freeswitch.pc
build/modmake.rules
libs/esl/Makefile
libs/esl/tests/Makefile
libs/esl/perl/Makefile
libs/esl/php/Makefile
libs/xmlrpc-c/include/xmlrpc-c/config.h
+1 -1
View File
@@ -1,5 +1,5 @@
AUTOMAKE_OPTIONS = foreign subdir-objects
SUBDIRS = . perl
SUBDIRS = . perl tests
MYLIB=./.libs/libesl.a
LIBS=-lncurses -lpthread -lm
LDFLAGS=-L. $(SYSTEM_LDFLAGS)
+14 -3
View File
@@ -1351,9 +1351,19 @@ ESL_DECLARE(esl_status_t) esl_recv_event(esl_handle_t *handle, int check_q, esl_
esl_ssize_t sofar = 0;
len = atol(cl);
body = malloc(len+1);
esl_assert(body);
*(body + len) = '\0';
if (len < 0 || len > ESL_MAX_CONTENT_LENGTH) {
esl_event_destroy(&revent);
goto fail;
}
body = malloc(len + 1);
if (!body) {
esl_event_destroy(&revent);
goto fail;
}
body[len] = '\0';
do {
esl_ssize_t r,s = esl_buffer_inuse(handle->packet_buf);
@@ -1367,6 +1377,7 @@ ESL_DECLARE(esl_status_t) esl_recv_event(esl_handle_t *handle, int check_q, esl_
if (!(strerror_r(handle->errnum, handle->err, sizeof(handle->err))))
*(handle->err)=0;
free(body);
esl_event_destroy(&revent);
goto fail;
} else if (r == 0) {
continue;
+2
View File
@@ -217,6 +217,8 @@ typedef enum {
#define esl_strlen_zero_buf(s) (*(s) == '\0')
#define end_of(_s) *(*_s == '\0' ? _s : _s + strlen(_s) - 1)
#define ESL_MAX_CONTENT_LENGTH (16 * 1024 * 1024)
#ifdef WIN32
#include <winsock2.h>
#include <windows.h>
+9
View File
@@ -0,0 +1,9 @@
AUTOMAKE_OPTIONS = foreign
noinst_PROGRAMS = test_recv_event
TESTS = $(noinst_PROGRAMS)
test_recv_event_SOURCES = test_recv_event.c
test_recv_event_CFLAGS = $(AM_CFLAGS) -I$(switch_srcdir)/libs/esl/src/include
test_recv_event_LDADD = $(top_builddir)/libs/esl/libesl.la
test_recv_event_LDFLAGS = $(AM_LDFLAGS) -lpthread -lm
+96
View File
@@ -0,0 +1,96 @@
/*
* test_recv_event.c
*
* Verifies that esl_recv_event() rejects out-of-range Content-Length
* values: negative numbers and values above ESL_MAX_CONTENT_LENGTH must
* cause the function to return ESL_FAIL and mark the handle as
* disconnected, leaving no allocated state behind.
*
* POSIX-only: uses socketpair(2). Returns 77 on Windows so automake
* marks the test as skipped.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
int main(void)
{
return 77;
}
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <esl.h>
#define TEST_ASSERT(cond) do { \
if (!(cond)) { \
fprintf(stderr, "FAIL %s:%d: %s\n", \
__FILE__, __LINE__, #cond); \
exit(1); \
} \
} while (0)
static void prepare_handle(esl_handle_t *h, esl_socket_t s)
{
memset(h, 0, sizeof(*h));
h->sock = s;
h->connected = 1;
TEST_ASSERT(esl_mutex_create(&h->mutex) == ESL_SUCCESS);
TEST_ASSERT(esl_buffer_create(&h->packet_buf,
BUF_CHUNK, BUF_START, 0) == ESL_SUCCESS);
}
static void expect_rejected(const char *frame, const char *desc)
{
int sv[2];
esl_handle_t h;
size_t n = strlen(frame);
ssize_t w;
fprintf(stderr, " case: %s\n", desc);
TEST_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
prepare_handle(&h, sv[0]);
w = write(sv[1], frame, n);
TEST_ASSERT(w == (ssize_t) n);
close(sv[1]);
TEST_ASSERT(esl_recv_event(&h, 0, NULL) == ESL_FAIL);
TEST_ASSERT(h.connected == 0);
esl_disconnect(&h);
}
int main(void)
{
fprintf(stderr, "test_recv_event: invalid Content-Length is rejected\n");
expect_rejected(
"Content-Type: text/event-plain\n"
"Content-Length: -1\n\n",
"negative Content-Length: -1");
expect_rejected(
"Content-Type: text/event-plain\n"
"Content-Length: -2\n\n",
"negative Content-Length: -2");
expect_rejected(
"Content-Type: text/event-plain\n"
"Content-Length: 99999999999\n\n",
"Content-Length above ESL_MAX_CONTENT_LENGTH");
fprintf(stderr, "OK\n");
return 0;
}
#endif
+2
View File
@@ -2075,6 +2075,7 @@ typedef uint32_t switch_io_flag_t;
SWITCH_EVENT_CALL_DETAIL
SWITCH_EVENT_DEVICE_STATE
SWITCH_EVENT_SHUTDOWN_REQUESTED - Shutdown of the system has been requested
SWITCH_EVENT_CERT_RELOAD - SSL/TLS certificates reload has been requested
SWITCH_EVENT_ALL - All events at once
</pre>
@@ -2172,6 +2173,7 @@ typedef enum {
SWITCH_EVENT_DEVICE_STATE,
SWITCH_EVENT_TEXT,
SWITCH_EVENT_SHUTDOWN_REQUESTED,
SWITCH_EVENT_CERT_RELOAD,
SWITCH_EVENT_ALL
} switch_event_types_t;
@@ -549,7 +549,7 @@ SWITCH_STANDARD_API(reg_url_function)
char *domain = NULL, *dup_domain = NULL;
char *concat = NULL;
const char *exclude_contact = NULL;
char *reply = "error/facility_not_subscribed";
char *reply;
switch_stream_handle_t mystream = { 0 };
if (!cmd) {
@@ -2324,7 +2324,7 @@ SWITCH_STANDARD_API(status_function)
int sps = 0, last_sps = 0, max_sps = 0, max_sps_fivemin = 0;
int sessions_peak = 0, sessions_peak_fivemin = 0; /* Max Concurrent Sessions buffers */
switch_bool_t html = SWITCH_FALSE; /* shortcut to format.html */
char * nl = "\n"; /* shortcut to format.nl */
char *nl; /* shortcut to format.nl */
stream_format format = { 0 };
switch_size_t cur = 0, max = 0;
@@ -2860,6 +2860,22 @@ SWITCH_STANDARD_API(reload_xml_function)
return SWITCH_STATUS_SUCCESS;
}
SWITCH_STANDARD_API(reload_cert_function)
{
switch_event_t *event;
if (switch_event_create(&event, SWITCH_EVENT_CERT_RELOAD) == SWITCH_STATUS_SUCCESS) {
switch_event_fire(&event);
stream->write_function(stream, "+OK cert reload event sent\n");
return SWITCH_STATUS_SUCCESS;
}
stream->write_function(stream, "-ERR failed to create event\n");
return SWITCH_STATUS_FALSE;
}
#define KILL_SYNTAX "<uuid> [cause]"
SWITCH_STANDARD_API(kill_function)
{
@@ -7523,7 +7539,7 @@ SWITCH_STANDARD_JSON_API(json_status_function)
SWITCH_STANDARD_API(json_function)
{
cJSON *jcmd = NULL, *format = NULL;
const char *message = "";
const char *message;
char *response = NULL;
if (zstr(cmd)) {
@@ -7653,9 +7669,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
SWITCH_ADD_API(commands_api_interface, "pool_stats", "Core pool memory usage", pool_stats_function, "Core pool memory usage.");
SWITCH_ADD_API(commands_api_interface, "quote_shell_arg", "Quote/escape a string for use on shell command line", quote_shell_arg_function, "<data>");
SWITCH_ADD_API(commands_api_interface, "regex", "Evaluate a regex", regex_function, "<data>|<pattern>[|<subst string>][n|b]");
SWITCH_ADD_API(commands_api_interface, "reloadacl", "Reload XML", reload_acl_function, "");
SWITCH_ADD_API(commands_api_interface, "reloadacl", "Reload ACL", reload_acl_function, "");
SWITCH_ADD_API(commands_api_interface, "reload", "Reload module", reload_function, UNLOAD_SYNTAX);
SWITCH_ADD_API(commands_api_interface, "reloadxml", "Reload XML", reload_xml_function, "");
SWITCH_ADD_API(commands_api_interface, "reloadcert", "Reload SSL/TLS certificates", reload_cert_function, "");
SWITCH_ADD_API(commands_api_interface, "replace", "Replace a string", replace_function, "<data>|<string1>|<string2>");
SWITCH_ADD_API(commands_api_interface, "say_string", "", say_string_function, SAY_STRING_SYNTAX);
SWITCH_ADD_API(commands_api_interface, "sched_api", "Schedule an api command", sched_api_function, SCHED_SYNTAX);
@@ -7831,6 +7848,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
switch_console_set_complete("add nat_map status");
switch_console_set_complete("add reload ::console::list_loaded_modules");
switch_console_set_complete("add reloadacl reloadxml");
switch_console_set_complete("add reloadcert");
switch_console_set_complete("add show aliases");
switch_console_set_complete("add show api");
switch_console_set_complete("add show application");
@@ -1911,8 +1911,8 @@ SWITCH_STANDARD_APP(conference_function)
member_flag_t mflags[MFLAG_MAX] = { 0 };
switch_core_session_message_t msg = { 0 };
uint8_t isbr = 0;
char *dpin = "";
const char *mdpin = "";
char *dpin;
const char *mdpin;
conference_xml_cfg_t xml_cfg = { 0 };
switch_event_t *params = NULL;
int locked = 0;
@@ -562,7 +562,7 @@ static void phase_e_handler(void *user_data, int result)
switch_event_t *event;
const char *var;
char *expanded;
const char *fax_result_str = "";
const char *fax_result_str;
pvt = (pvt_t *) user_data;
switch_assert(pvt);
+8 -2
View File
@@ -15,7 +15,7 @@ mod_sofia_la_SOURCES =
mod_sofia_la_LIBADD = $(switch_builddir)/libfreeswitch.la libsofiamod.la
mod_sofia_la_LDFLAGS = -avoid-version -module -no-undefined -shared $(SOFIA_SIP_LIBS) $(STIRSHAKEN_LIBS)
noinst_PROGRAMS = test/test_sofia_funcs test/test_nuafail test/sipp-based-tests
noinst_PROGRAMS = test/test_sofia_funcs test/test_nuafail test/sipp-based-tests test/test_603plus
test_test_sofia_funcs_SOURCES = test/test_sofia_funcs.c
test_test_sofia_funcs_CFLAGS = $(AM_CFLAGS) $(SOFIA_SIP_CFLAGS) $(STIRSHAKEN_CFLAGS) -DSWITCH_TEST_BASE_DIR_FOR_CONF=\"${abs_builddir}/test\" -DSWITCH_TEST_BASE_DIR_OVERRIDE=\"${abs_builddir}/test\"
@@ -25,6 +25,11 @@ endif
test_test_sofia_funcs_LDFLAGS = $(AM_LDFLAGS) -avoid-version -no-undefined $(freeswitch_LDFLAGS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS) $(STIRSHAKEN_LIBS)
test_test_sofia_funcs_LDADD = libsofiamod.la $(SOFIA_SIP_LIBS) $(STIRSHAKEN_LIBS)
test_test_603plus_SOURCES = test/test_603plus.c
test_test_603plus_CFLAGS = $(AM_CFLAGS) $(SOFIA_SIP_CFLAGS) -DSWITCH_TEST_BASE_DIR_FOR_CONF=\"${abs_builddir}/test\" -DSWITCH_TEST_BASE_DIR_OVERRIDE=\"${abs_builddir}/test\"
test_test_603plus_LDFLAGS = $(AM_LDFLAGS) -avoid-version -no-undefined $(freeswitch_LDFLAGS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS)
test_test_603plus_LDADD = libsofiamod.la $(SOFIA_SIP_LIBS)
test_test_nuafail_SOURCES = test/test_nuafail.c
test_test_nuafail_CFLAGS = $(AM_CFLAGS) $(SOFIA_SIP_CFLAGS) $(STIRSHAKEN_CFLAGS) -DSWITCH_TEST_BASE_DIR_FOR_CONF=\"${abs_builddir}/test\" -DSWITCH_TEST_BASE_DIR_OVERRIDE=\"${abs_builddir}/test\"
if HAVE_STIRSHAKEN
@@ -38,13 +43,14 @@ test_sipp_based_tests_CFLAGS = $(AM_CFLAGS) $(SOFIA_SIP_CFLAGS) -DSWITCH_TEST_BA
test_sipp_based_tests_LDFLAGS = $(AM_LDFLAGS) -avoid-version -no-undefined $(freeswitch_LDFLAGS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS)
test_sipp_based_tests_LDADD = libsofiamod.la $(SOFIA_SIP_LIBS)
TESTS = test/test_sofia_funcs.sh test/test_nuafail
TESTS = test/test_sofia_funcs.sh test/test_nuafail test/test_603plus
#TESTS += test/test_run_sipp.sh
if ISMAC
mod_sofia_la_LDFLAGS += -framework CoreFoundation -framework SystemConfiguration
test_test_sofia_funcs_LDFLAGS += -framework CoreFoundation -framework SystemConfiguration
test_test_nuafail_LDFLAGS += -framework CoreFoundation -framework SystemConfiguration
test_test_603plus_LDFLAGS += -framework CoreFoundation -framework SystemConfiguration
test_sipp_based_tests_LDFLAGS += -framework CoreFoundation -framework SystemConfiguration
endif
+67 -1
View File
@@ -493,6 +493,7 @@ switch_status_t sofia_on_hangup(switch_core_session_t *session)
const char *val = NULL;
const char *max_forwards = switch_channel_get_variable(channel, SWITCH_MAX_FORWARDS_VARIABLE);
const char *call_info = switch_channel_get_variable(channel, "presence_call_info_full");
const char *passthrough_603plus = switch_channel_get_variable(channel, "sip_603plus_passthrough");
const char *session_id_header = sofia_glue_session_id_header(session, tech_pvt->profile);
val = switch_channel_get_variable(tech_pvt->channel, "disable_q850_reason");
@@ -512,6 +513,23 @@ switch_status_t sofia_on_hangup(switch_core_session_t *session)
}
}
/* 603+ (ATIS-1000099) Reason header override — applied after standard reason construction.
*
* passthrough=true: Restore 603+ Reason even if disable_q850_reason suppressed it.
* Allows selective forwarding of 603+ while suppressing other Reason headers.
* passthrough=false: Strip Reason header entirely send clean 603 Decline with no Reason. */
if (passthrough_603plus) {
const char *reason_603plus = switch_channel_get_variable(channel, "sip_603plus_reason");
if (!zstr(reason_603plus)) {
if (switch_true(passthrough_603plus)) {
reason = switch_core_session_sprintf(session, "%s", reason_603plus);
} else if (switch_false(passthrough_603plus)) {
reason = switch_core_session_sprintf(session, "");
}
}
}
if (switch_channel_test_flag(channel, CF_INTERCEPT) || cause == SWITCH_CAUSE_PICKED_OFF || cause == SWITCH_CAUSE_LOSE_RACE) {
switch_channel_set_variable(channel, "call_completed_elsewhere", "true");
}
@@ -557,6 +575,11 @@ switch_status_t sofia_on_hangup(switch_core_session_t *session)
if (tech_pvt->respond_phrase) {
//phrase = su_strdup(nua_handle_home(tech_pvt->nh), tech_pvt->respond_phrase);
phrase = tech_pvt->respond_phrase;
} else if (sip_cause == 603
&& !zstr(reason)
&& switch_true(passthrough_603plus)
&& !zstr(switch_channel_get_variable(channel, "sip_603plus_reason"))) {
phrase = "Network Blocked";
} else {
phrase = sip_status_phrase(sip_cause);
}
@@ -4091,7 +4114,7 @@ SWITCH_STANDARD_API(sofia_contact_function)
sofia_profile_t *profile = NULL;
const char *exclude_contact = NULL;
const char *match_user_agent = NULL;
char *reply = "error/facility_not_subscribed";
char *reply;
switch_stream_handle_t mystream = { 0 };
if (!cmd) {
@@ -6528,6 +6551,42 @@ char *sofia_stir_shaken_as_create_identity_header(switch_core_session_t *session
}
#ifdef HAVE_NUA_RELOAD_TLS
static void sofia_cert_reload_handler(switch_event_t *event)
{
switch_hash_index_t *hi;
const void *vvar;
void *val;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Certificate reload event received, processing\n");
switch_mutex_lock(mod_sofia_globals.hash_mutex);
for (hi = switch_core_hash_first(mod_sofia_globals.profile_hash); hi; hi = switch_core_hash_next(&hi)) {
sofia_profile_t *profile;
switch_core_hash_this(hi, &vvar, NULL, &val);
profile = (sofia_profile_t *) val;
if (!sofia_test_pflag(profile, PFLAG_RUNNING) || !profile->nua || !profile->tls_cert_dir) {
continue;
}
if (strcmp(vvar, profile->name)) {
continue;
}
nua_reload_tls(profile->nua, profile->tls_cert_dir);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "TLS certificate reload signaled for sofia profile %s\n", profile->name);
}
switch_mutex_unlock(mod_sofia_globals.hash_mutex);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Certificate reload event processed\n");
}
#endif
SWITCH_MODULE_LOAD_FUNCTION(mod_sofia_load)
{
switch_chat_interface_t *chat_interface;
@@ -6694,6 +6753,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_sofia_load)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Waiting for profiles to start\n");
switch_yield(1500000);
#ifdef HAVE_NUA_RELOAD_TLS
switch_event_bind(modname, SWITCH_EVENT_CERT_RELOAD, SWITCH_EVENT_SUBCLASS_ANY, sofia_cert_reload_handler, NULL);
#endif
if (switch_event_bind(modname, SWITCH_EVENT_CUSTOM, MULTICAST_EVENT, event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n");
switch_goto_status(SWITCH_STATUS_TERM, err);
@@ -6876,6 +6939,9 @@ void mod_sofia_shutdown_cleanup(void) {
}
switch_mutex_unlock(mod_sofia_globals.mutex);
#ifdef HAVE_NUA_RELOAD_TLS
switch_event_unbind_callback(sofia_cert_reload_handler);
#endif
switch_event_unbind_callback(sofia_presence_event_handler);
switch_event_unbind_callback(general_queue_event_handler);
-1
View File
@@ -168,7 +168,6 @@ typedef struct sofia_dispatch_event_s {
int save;
switch_core_session_t *session;
switch_core_session_t *init_session;
switch_memory_pool_t *pool;
struct sofia_dispatch_event_s *next;
} sofia_dispatch_event_t;
+31 -19
View File
@@ -1210,7 +1210,7 @@ void sofia_update_callee_id(switch_core_session_t *session, sofia_profile_t *pro
switch_channel_t *channel = switch_core_session_get_channel(session);
sip_p_asserted_identity_t *passerted = NULL;
char *name = NULL;
const char *number = "unknown", *tmp;
const char *number, *tmp;
switch_caller_profile_t *caller_profile;
char *dup = NULL;
switch_event_t *event;
@@ -2199,22 +2199,15 @@ static uint32_t DE_THREAD_CNT = 0;
void *SWITCH_THREAD_FUNC sofia_msg_thread_run_once(switch_thread_t *thread, void *obj)
{
sofia_dispatch_event_t *de = (sofia_dispatch_event_t *) obj;
switch_memory_pool_t *pool = NULL;
switch_mutex_lock(mod_sofia_globals.mutex);
DE_THREAD_CNT++;
switch_mutex_unlock(mod_sofia_globals.mutex);
if (de) {
pool = de->pool;
de->pool = NULL;
sofia_process_dispatch_event(&de);
}
if (pool) {
switch_core_destroy_memory_pool(&pool);
}
switch_mutex_lock(mod_sofia_globals.mutex);
DE_THREAD_CNT--;
switch_mutex_unlock(mod_sofia_globals.mutex);
@@ -2225,16 +2218,12 @@ void *SWITCH_THREAD_FUNC sofia_msg_thread_run_once(switch_thread_t *thread, void
void sofia_process_dispatch_event_in_thread(sofia_dispatch_event_t **dep)
{
sofia_dispatch_event_t *de = *dep;
switch_memory_pool_t *pool;
//sofia_profile_t *profile = (*dep)->profile;
switch_thread_data_t *td;
switch_core_new_memory_pool(&pool);
*dep = NULL;
de->pool = pool;
td = switch_core_alloc(pool, sizeof(*td));
switch_zmalloc(td, sizeof(*td));
td->alloc = 1;
td->func = sofia_msg_thread_run_once;
td->obj = de;
@@ -2460,13 +2449,15 @@ void sofia_event_callback(nua_event_t event,
}
if (!sofia_private) {
if (sess_count >= sess_max || !sofia_test_pflag(profile, PFLAG_RUNNING) || !switch_core_ready_inbound()) {
int unavailable = (sess_count >= sess_max || !sofia_test_pflag(profile, PFLAG_RUNNING) || !switch_core_ready_inbound());
int bypass = (event == nua_i_options && !sofia_test_pflag(profile, PFLAG_OPTIONS_RESPOND_503_ON_BUSY));
if (unavailable && !bypass) {
nua_respond(nh, 503, "Maximum Calls In Progress", SIPTAG_RETRY_AFTER_STR("300"), NUTAG_WITH_THIS(nua), TAG_END());
nua_handle_destroy(nh);
goto end;
}
if (switch_queue_size(mod_sofia_globals.msg_queue) > (unsigned int)critical) {
nua_respond(nh, 503, "System Busy", SIPTAG_RETRY_AFTER_STR("300"), NUTAG_WITH_THIS(nua), TAG_END());
nua_handle_destroy(nh);
@@ -6654,15 +6645,36 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status
}
if (status >= 400) {
char *reason_header = NULL;
char status_str[5];
switch_snprintf(status_str, sizeof(status_str), "%d", status);
switch_channel_set_variable(channel, "sip_invite_failure_status", status_str);
switch_channel_set_variable(channel, "sip_invite_failure_phrase", phrase);
switch_channel_set_variable_partner(channel, "sip_invite_failure_status", status_str);
switch_channel_set_variable_partner(channel, "sip_invite_failure_phrase", phrase);
reason_header = sip_header_as_string(nua_handle_get_home(nh), (void *) sip->sip_reason);
if (!zstr(reason_header)) {
switch_channel_set_variable(channel, "sip_reason", reason_header);
switch_channel_set_variable_partner(channel, "sip_reason", reason_header);
}
/* 603+ (ATIS-1000099) detection: clear stale state from serial forking, then check */
switch_channel_set_variable(channel, "sip_603plus_reason", NULL);
switch_channel_set_variable_partner(channel, "sip_603plus_reason", NULL);
if (status == 603 && phrase && !strcasecmp(phrase, "Network Blocked")
&& sip->sip_reason && sip->sip_reason->re_text
&& !strncmp(sip->sip_reason->re_text, "\"v=analytics1;", 14)
&& !zstr(reason_header)) {
switch_channel_set_variable(channel, "sip_603plus_reason", reason_header);
switch_channel_set_variable_partner(channel, "sip_603plus_reason", reason_header);
}
} else {
switch_channel_set_variable_partner(channel, "sip_invite_failure_status", NULL);
switch_channel_set_variable_partner(channel, "sip_invite_failure_phrase", NULL);
switch_channel_set_variable_partner(channel, "sip_603plus_reason", NULL);
}
if (status >= 400 && sip->sip_reason && sip->sip_reason->re_protocol && (!strcasecmp(sip->sip_reason->re_protocol, "Q.850")
@@ -7533,7 +7545,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
}
if (channel && profile->pres_type && ss_state == nua_callstate_ready && status == 200) {
const char* to_tag = "";
const char* to_tag;
char *sql = NULL;
to_tag = switch_str_nil(switch_channel_get_variable(channel, "sip_to_tag"));
sql = switch_mprintf("update sip_dialogs set sip_to_tag='%q' "
@@ -10390,7 +10402,7 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia
nua_handle_t *bnh = NULL;
char sip_acl_authed_by[512] = "";
char sip_acl_token[512] = "";
const char *dialog_from_user = "", *dialog_from_host = "", *to_user = "", *to_host = "", *contact_user = "", *contact_host = "";
const char *dialog_from_user = "", *dialog_from_host = "", *to_user = "", *to_host = "", *contact_user, *contact_host;
const char *user_agent = "", *call_id = "";
url_t *from = NULL, *to = NULL, *contact = NULL;
const char *to_tag = "";
@@ -11633,7 +11645,7 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia
if (profile->pres_type) {
const char *presence_data = switch_channel_get_variable(channel, "presence_data");
const char *presence_id = switch_channel_get_variable(channel, "presence_id");
char *full_contact = "";
char *full_contact;
char *p = NULL;
time_t now;
+2 -2
View File
@@ -1038,7 +1038,7 @@ switch_status_t sofia_glue_do_invite(switch_core_session_t *session)
switch_caller_profile_t *caller_profile;
const char *cid_name, *cid_num;
char *e_dest = NULL;
const char *holdstr = "";
const char *holdstr;
char *extra_headers = NULL;
switch_status_t status = SWITCH_STATUS_FALSE;
uint32_t session_timeout = tech_pvt->profile->session_timeout;
@@ -3334,7 +3334,7 @@ char *sofia_glue_gen_contact_str(sofia_profile_t *profile, sip_t const *sip, nua
const char *contact_host;//, *contact_user;
sip_contact_t const *contact;
char *port;
const char *display = "\"user\"";
const char *display;
char new_port[25] = "";
sofia_nat_parse_t lnp = { { 0 } };
const char *ipv6;
+2 -2
View File
@@ -2392,7 +2392,7 @@ static int sofia_dialog_probe_notify_callback(void *pArg, int argc, char **argv,
switch_stream_handle_t stream = { 0 };
char *to;
const char *pl = NULL;
const char *ct = "application/dialog-info+xml";
const char *ct;
if (mod_sofia_globals.debug_presence > 0) {
int i;
@@ -3659,7 +3659,7 @@ void sofia_presence_handle_sip_i_subscribe(int status,
char *orig_proto = "";
char *alt_proto = NULL;
char *d_user = NULL;
char *contact_str = "";
char *contact_str;
const char *call_id = NULL;
char *to_str = NULL;
char *full_from = NULL;
@@ -140,6 +140,90 @@
<action application="park" data=""/>
</condition>
</extension>
<!-- 603+ (ATIS-1000099) detection tests -->
<extension name="603plus_detect_valid_sip">
<condition field="destination_number" expression="^\+15553336050$">
<action application="set" data="sip_reason=SIP;cause=603;text=&quot;v=analytics1;url=https://example.com/redress&quot;;location=TN"/>
<action application="respond" data="603 Network Blocked"/>
</condition>
</extension>
<extension name="603plus_detect_valid_q850">
<condition field="destination_number" expression="^\+15553336051$">
<action application="set" data="sip_reason=Q.850;cause=21;text=&quot;v=analytics1;url=https://example.com/redress&quot;;location=LN"/>
<action application="respond" data="603 Network Blocked"/>
</condition>
</extension>
<extension name="603plus_detect_wrong_phrase">
<condition field="destination_number" expression="^\+15553336052$">
<action application="set" data="sip_reason=SIP;cause=603;text=&quot;v=analytics1;url=https://example.com/redress&quot;;location=TN"/>
<action application="respond" data="603 Decline"/>
</condition>
</extension>
<extension name="603plus_detect_no_analytics">
<condition field="destination_number" expression="^\+15553336053$">
<action application="set" data="sip_reason=Q.850;cause=21;text=&quot;Call Rejected&quot;"/>
<action application="respond" data="603 Network Blocked"/>
</condition>
</extension>
<extension name="603plus_detect_no_reason">
<condition field="destination_number" expression="^\+15553336054$">
<action application="set" data="disable_q850_reason=true"/>
<action application="respond" data="603 Network Blocked"/>
</condition>
</extension>
<extension name="603plus_detect_non_603">
<condition field="destination_number" expression="^\+15553336055$">
<action application="set" data="sip_reason=SIP;cause=603;text=&quot;v=analytics1;url=https://example.com/redress&quot;;location=TN"/>
<action application="respond" data="486 Busy Here"/>
</condition>
</extension>
<extension name="603plus_detect_after_180">
<condition field="destination_number" expression="^\+15553336056$">
<action application="set" data="sip_reason=SIP;cause=603;text=&quot;v=analytics1;url=https://example.com/redress&quot;;location=TN"/>
<action application="ring_ready"/>
<action application="sleep" data="500"/>
<action application="respond" data="603 Network Blocked"/>
</condition>
</extension>
<!-- 603+ passthrough tests: bridge to 603+ target, vary passthrough setting -->
<extension name="603plus_passthrough_true">
<condition field="destination_number" expression="^\+15553336060$">
<action application="set" data="sip_603plus_passthrough=true"/>
<action application="bridge" data="sofia/gateway/test/+15553336050"/>
</condition>
</extension>
<extension name="603plus_passthrough_false">
<condition field="destination_number" expression="^\+15553336061$">
<action application="set" data="sip_603plus_passthrough=false"/>
<action application="bridge" data="sofia/gateway/test/+15553336050"/>
</condition>
</extension>
<extension name="603plus_passthrough_default">
<condition field="destination_number" expression="^\+15553336062$">
<action application="bridge" data="sofia/gateway/test/+15553336050"/>
</condition>
</extension>
<!-- 603+ passthrough + disable_q850_reason combination tests -->
<extension name="603plus_disable_reason_passthrough_true">
<condition field="destination_number" expression="^\+15553336063$">
<action application="set" data="disable_q850_reason=true"/>
<action application="set" data="sip_603plus_passthrough=true"/>
<action application="bridge" data="sofia/gateway/test/+15553336050"/>
</condition>
</extension>
<extension name="603plus_disable_reason_passthrough_false">
<condition field="destination_number" expression="^\+15553336064$">
<action application="set" data="disable_q850_reason=true"/>
<action application="set" data="sip_603plus_passthrough=false"/>
<action application="bridge" data="sofia/gateway/test/+15553336050"/>
</condition>
</extension>
<extension name="603plus_disable_reason_passthrough_default">
<condition field="destination_number" expression="^\+15553336065$">
<action application="set" data="disable_q850_reason=true"/>
<action application="bridge" data="sofia/gateway/test/+15553336050"/>
</condition>
</extension>
</context>
</section>
</document>
@@ -0,0 +1,490 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2026, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Dmitry Verenitsin <dmitry.verenitsin@signalwire.com>
*
*
* test_603plus.c -- Tests for SIP 603+ (ATIS-1000099) detection and passthrough
*
* Detection requires BOTH:
* 1. SIP status 603 with phrase "Network Blocked" (case-insensitive)
* 2. Reason header text starts with "v=analytics1;" (ATIS version AVP)
*
* Test approach: originate via loopback gateway (same FS instance).
* The responding extension sends a crafted 603 with/without Reason header.
* We bind to CHANNEL_HANGUP_COMPLETE to capture sip_603plus_reason from
* the outbound leg before it is destroyed.
*
* Passthrough tests use a bridge scenario: originate -> middle extension
* (sets passthrough) -> bridges to 603+ target. The originate leg receives
* the response FROM the middle box, letting us verify what was actually sent.
*/
#include <switch.h>
#include <test/switch_test.h>
/* Event capture state */
static struct {
char sip_603plus_reason[1024];
char sip_invite_failure_phrase[256];
char sip_reason[1024];
switch_bool_t received;
} capture;
static void reset_capture(void)
{
memset(capture.sip_603plus_reason, 0, sizeof(capture.sip_603plus_reason));
memset(capture.sip_invite_failure_phrase, 0, sizeof(capture.sip_invite_failure_phrase));
memset(capture.sip_reason, 0, sizeof(capture.sip_reason));
capture.received = SWITCH_FALSE;
}
static void on_hangup_complete(switch_event_t *event)
{
const char *direction, *val;
/* Only capture from outbound legs (the originating call, not the responder).
* In bridge tests, multiple outbound legs hang up (bridge B-leg, then originate O-leg).
* Reset on every outbound event so the last one (O-leg) wins cleanly. */
direction = switch_event_get_header(event, "Call-Direction");
if (zstr(direction) || strcmp(direction, "outbound")) return;
reset_capture();
val = switch_event_get_header(event, "variable_sip_603plus_reason");
if (!zstr(val)) {
switch_snprintf(capture.sip_603plus_reason, sizeof(capture.sip_603plus_reason), "%s", val);
}
val = switch_event_get_header(event, "variable_sip_invite_failure_phrase");
if (!zstr(val)) {
switch_snprintf(capture.sip_invite_failure_phrase, sizeof(capture.sip_invite_failure_phrase), "%s", val);
}
val = switch_event_get_header(event, "variable_sip_reason");
if (!zstr(val)) {
switch_snprintf(capture.sip_reason, sizeof(capture.sip_reason), "%s", val);
}
capture.received = SWITCH_TRUE;
}
static void originate_and_wait(const char *dest, switch_call_cause_t *cause)
{
switch_core_session_t *session = NULL;
switch_ivr_originate(NULL, &session, cause,
dest, 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL);
if (session) {
switch_channel_hangup(switch_core_session_get_channel(session), SWITCH_CAUSE_NORMAL_CLEARING);
switch_core_session_rwunlock(session);
}
/* Let event dispatch thread deliver CHANNEL_HANGUP_COMPLETE */
switch_yield(1000000);
}
/* Test suite */
FST_CORE_EX_BEGIN("./conf", SCF_VG | SCF_USE_SQL)
{
FST_MODULE_BEGIN(mod_sofia, sofia)
{
FST_SETUP_BEGIN()
{
}
FST_SETUP_END()
FST_TEARDOWN_BEGIN()
{
}
FST_TEARDOWN_END()
/* Detection: positive cases */
FST_TEST_BEGIN(detect_valid_603plus_sip)
{
/*
* Extension +15553336050 sends:
* 603 Network Blocked
* Reason: SIP;cause=603;text="v=analytics1;url=https://example.com/redress";location=TN
*
* Both conditions met -> sip_603plus_reason MUST be set.
*/
switch_call_cause_t cause;
switch_event_bind("test_603plus", SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE,
SWITCH_EVENT_SUBCLASS_ANY, on_hangup_complete, NULL);
reset_capture();
originate_and_wait("sofia/gateway/test/+15553336050", &cause);
fst_xcheck(cause == SWITCH_CAUSE_CALL_REJECTED, "Expected CALL_REJECTED for 603");
fst_xcheck(capture.received == SWITCH_TRUE, "Should have received outbound hangup event");
fst_xcheck(!zstr_buf(capture.sip_603plus_reason), "sip_603plus_reason must be set for valid 603+");
fst_xcheck(!!strstr(capture.sip_603plus_reason, "v=analytics1"), "sip_603plus_reason must contain v=analytics1");
fst_xcheck(!strcasecmp(capture.sip_invite_failure_phrase, "Network Blocked"), "Failure phrase must be 'Network Blocked'");
switch_event_unbind_callback(on_hangup_complete);
}
FST_TEST_END()
FST_TEST_BEGIN(detect_valid_603plus_q850)
{
/*
* Extension +15553336051 sends:
* 603 Network Blocked
* Reason: Q.850;cause=21;text="v=analytics1;url=https://example.com/redress";location=LN
*
* Q.850 protocol is equally valid per ATIS-1000099 section 4.1.1.
*/
switch_call_cause_t cause;
switch_event_bind("test_603plus", SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE,
SWITCH_EVENT_SUBCLASS_ANY, on_hangup_complete, NULL);
reset_capture();
originate_and_wait("sofia/gateway/test/+15553336051", &cause);
fst_xcheck(cause == SWITCH_CAUSE_CALL_REJECTED, "Expected CALL_REJECTED for 603");
fst_xcheck(capture.received == SWITCH_TRUE, "Should have received outbound hangup event");
fst_xcheck(!zstr_buf(capture.sip_603plus_reason), "sip_603plus_reason must be set for Q.850 ATIS Reason");
fst_xcheck(!!strstr(capture.sip_603plus_reason, "v=analytics1"), "sip_603plus_reason must contain v=analytics1");
switch_event_unbind_callback(on_hangup_complete);
}
FST_TEST_END()
FST_TEST_BEGIN(detect_603plus_after_180)
{
/*
* Extension +15553336056 sends 180 Ringing, waits 500ms, then:
* 603 Network Blocked
* Reason: SIP;cause=603;text="v=analytics1;url=https://example.com/redress";location=TN
*
* Detection must work after provisional responses.
*/
switch_call_cause_t cause;
switch_event_bind("test_603plus", SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE,
SWITCH_EVENT_SUBCLASS_ANY, on_hangup_complete, NULL);
reset_capture();
originate_and_wait("sofia/gateway/test/+15553336056", &cause);
fst_xcheck(cause == SWITCH_CAUSE_CALL_REJECTED, "Expected CALL_REJECTED for 603");
fst_xcheck(capture.received == SWITCH_TRUE, "Should have received outbound hangup event");
fst_xcheck(!zstr_buf(capture.sip_603plus_reason), "sip_603plus_reason must be set after 180+603");
fst_xcheck(!!strstr(capture.sip_603plus_reason, "v=analytics1"), "sip_603plus_reason must contain v=analytics1");
switch_event_unbind_callback(on_hangup_complete);
}
FST_TEST_END()
/* Detection: negative cases */
FST_TEST_BEGIN(detect_wrong_phrase)
{
/*
* Extension +15553336052 sends:
* 603 Decline <- wrong phrase
* Reason: SIP;cause=603;text="v=analytics1;url=https://example.com/redress";location=TN
*
* Phrase is "Decline", not "Network Blocked". Detection must NOT fire.
*/
switch_call_cause_t cause;
switch_event_bind("test_603plus", SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE,
SWITCH_EVENT_SUBCLASS_ANY, on_hangup_complete, NULL);
reset_capture();
originate_and_wait("sofia/gateway/test/+15553336052", &cause);
fst_xcheck(cause == SWITCH_CAUSE_CALL_REJECTED, "Expected CALL_REJECTED for 603");
fst_xcheck(capture.received == SWITCH_TRUE, "Should have received outbound hangup event");
fst_xcheck(zstr_buf(capture.sip_603plus_reason), "sip_603plus_reason must NOT be set when phrase is 'Decline'");
/* sip_reason should still be set (existing behavior for any Reason header) */
fst_xcheck(!zstr_buf(capture.sip_reason), "sip_reason should be set regardless of phrase");
switch_event_unbind_callback(on_hangup_complete);
}
FST_TEST_END()
FST_TEST_BEGIN(detect_no_analytics_in_reason)
{
/*
* Extension +15553336053 sends:
* 603 Network Blocked
* Reason: Q.850;cause=21;text="Call Rejected" <- no v=analytics1
*
* Reason header lacks v=analytics1. Detection must NOT fire.
*/
switch_call_cause_t cause;
switch_event_bind("test_603plus", SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE,
SWITCH_EVENT_SUBCLASS_ANY, on_hangup_complete, NULL);
reset_capture();
originate_and_wait("sofia/gateway/test/+15553336053", &cause);
fst_xcheck(cause == SWITCH_CAUSE_CALL_REJECTED, "Expected CALL_REJECTED for 603");
fst_xcheck(capture.received == SWITCH_TRUE, "Should have received outbound hangup event");
fst_xcheck(zstr_buf(capture.sip_603plus_reason), "sip_603plus_reason must NOT be set without v=analytics1");
switch_event_unbind_callback(on_hangup_complete);
}
FST_TEST_END()
FST_TEST_BEGIN(detect_no_reason_header)
{
/*
* Extension +15553336054 sends:
* 603 Network Blocked
* (no Reason header -- disable_q850_reason=true suppresses it)
*
* No Reason header -> sip->sip_reason is NULL. Detection must NOT fire.
*/
switch_call_cause_t cause;
switch_event_bind("test_603plus", SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE,
SWITCH_EVENT_SUBCLASS_ANY, on_hangup_complete, NULL);
reset_capture();
originate_and_wait("sofia/gateway/test/+15553336054", &cause);
fst_xcheck(cause == SWITCH_CAUSE_CALL_REJECTED, "Expected CALL_REJECTED for 603");
fst_xcheck(capture.received == SWITCH_TRUE, "Should have received outbound hangup event");
fst_xcheck(zstr_buf(capture.sip_603plus_reason), "sip_603plus_reason must NOT be set without Reason header");
fst_xcheck(zstr_buf(capture.sip_reason), "sip_reason should not be set when Reason header is suppressed");
switch_event_unbind_callback(on_hangup_complete);
}
FST_TEST_END()
FST_TEST_BEGIN(detect_non_603_status)
{
/*
* Extension +15553336055 sends:
* 486 Busy Here <- not 603
* Reason: SIP;cause=603;text="v=analytics1;url=https://example.com/redress";location=TN
*
* Status code is 486, not 603. Detection must NOT fire.
*/
switch_call_cause_t cause;
switch_event_bind("test_603plus", SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE,
SWITCH_EVENT_SUBCLASS_ANY, on_hangup_complete, NULL);
reset_capture();
originate_and_wait("sofia/gateway/test/+15553336055", &cause);
fst_xcheck(cause == SWITCH_CAUSE_USER_BUSY, "Expected USER_BUSY for 486");
fst_xcheck(capture.received == SWITCH_TRUE, "Should have received outbound hangup event");
fst_xcheck(zstr_buf(capture.sip_603plus_reason), "sip_603plus_reason must NOT be set for non-603 status");
switch_event_unbind_callback(on_hangup_complete);
}
FST_TEST_END()
/*
* Passthrough behavior.
*
* Bridge scenario: originate -> middle extension (sets passthrough) -> bridges to 603+ target.
* The originate leg receives the response FROM the middle box. We capture its
* sip_invite_failure_phrase and sip_reason to verify what was actually sent.
*/
FST_TEST_BEGIN(passthrough_true)
{
/*
* Extension +15553336060 sets sip_603plus_passthrough=true, bridges to 603+ target.
* The middle box should forward both "Network Blocked" phrase and ATIS Reason header.
* Our originate leg should see a valid 603+.
*/
switch_call_cause_t cause;
switch_event_bind("test_603plus", SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE,
SWITCH_EVENT_SUBCLASS_ANY, on_hangup_complete, NULL);
reset_capture();
originate_and_wait("sofia/gateway/test/+15553336060", &cause);
fst_xcheck(cause == SWITCH_CAUSE_CALL_REJECTED, "Expected CALL_REJECTED for 603");
fst_xcheck(capture.received == SWITCH_TRUE, "Should have received outbound hangup event");
fst_xcheck(!strcasecmp(capture.sip_invite_failure_phrase, "Network Blocked"),
"passthrough=true must preserve 'Network Blocked' phrase");
fst_xcheck(!zstr_buf(capture.sip_603plus_reason),
"passthrough=true must result in valid 603+ on originate leg");
switch_event_unbind_callback(on_hangup_complete);
}
FST_TEST_END()
FST_TEST_BEGIN(passthrough_false)
{
/*
* Extension +15553336061 sets sip_603plus_passthrough=false, bridges to 603+ target.
* The middle box should strip the ATIS Reason and use default phrase "Decline".
* Our originate leg should NOT see a 603+.
*/
switch_call_cause_t cause;
switch_event_bind("test_603plus", SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE,
SWITCH_EVENT_SUBCLASS_ANY, on_hangup_complete, NULL);
reset_capture();
originate_and_wait("sofia/gateway/test/+15553336061", &cause);
fst_xcheck(cause == SWITCH_CAUSE_CALL_REJECTED, "Expected CALL_REJECTED for 603");
fst_xcheck(capture.received == SWITCH_TRUE, "Should have received outbound hangup event");
fst_xcheck(strcasecmp(capture.sip_invite_failure_phrase, "Network Blocked") != 0,
"passthrough=false must NOT send 'Network Blocked' phrase");
fst_xcheck(zstr_buf(capture.sip_603plus_reason),
"passthrough=false must strip ATIS Reason (no 603+ on originate leg)");
fst_xcheck(zstr_buf(capture.sip_reason),
"passthrough=false must suppress Reason header entirely");
switch_event_unbind_callback(on_hangup_complete);
}
FST_TEST_END()
FST_TEST_BEGIN(passthrough_default)
{
/*
* Extension +15553336062 does NOT set sip_603plus_passthrough, bridges to 603+ target.
* Default: phrase is "Decline" (existing behavior), but ATIS Reason leaks through.
* This is the backward-compatible state.
*/
switch_call_cause_t cause;
switch_event_bind("test_603plus", SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE,
SWITCH_EVENT_SUBCLASS_ANY, on_hangup_complete, NULL);
reset_capture();
originate_and_wait("sofia/gateway/test/+15553336062", &cause);
fst_xcheck(cause == SWITCH_CAUSE_CALL_REJECTED, "Expected CALL_REJECTED for 603");
fst_xcheck(capture.received == SWITCH_TRUE, "Should have received outbound hangup event");
fst_xcheck(strcasecmp(capture.sip_invite_failure_phrase, "Network Blocked") != 0,
"default passthrough must NOT change phrase (stays 'Decline')");
/* ATIS Reason leaks through via sip_reason -- this is existing behavior */
fst_xcheck(!zstr_buf(capture.sip_reason),
"default passthrough: sip_reason should still be set (existing behavior)");
switch_event_unbind_callback(on_hangup_complete);
}
FST_TEST_END()
/*
* disable_q850_reason + passthrough combinations.
*
* Tests that disable_q850_reason and sip_603plus_passthrough work independently.
* disable_q850_reason suppresses standard Reason headers;
* sip_603plus_passthrough controls 603+ ATIS Reason forwarding.
*/
FST_TEST_BEGIN(disable_reason_passthrough_true)
{
/*
* Extension +15553336063: disable_q850_reason=true + sip_603plus_passthrough=true.
* Standard Reason suppressed, but ATIS 603+ Reason restored.
* The customer use case: suppress all Reason headers except FCC-required 603+.
*/
switch_call_cause_t cause;
switch_event_bind("test_603plus", SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE,
SWITCH_EVENT_SUBCLASS_ANY, on_hangup_complete, NULL);
reset_capture();
originate_and_wait("sofia/gateway/test/+15553336063", &cause);
fst_xcheck(cause == SWITCH_CAUSE_CALL_REJECTED, "Expected CALL_REJECTED for 603");
fst_xcheck(capture.received == SWITCH_TRUE, "Should have received outbound hangup event");
fst_xcheck(!strcasecmp(capture.sip_invite_failure_phrase, "Network Blocked"),
"disable_q850+passthrough=true must preserve 'Network Blocked' phrase");
fst_xcheck(!zstr_buf(capture.sip_603plus_reason),
"disable_q850+passthrough=true must restore ATIS Reason");
switch_event_unbind_callback(on_hangup_complete);
}
FST_TEST_END()
FST_TEST_BEGIN(disable_reason_passthrough_false)
{
/*
* Extension +15553336064: disable_q850_reason=true + sip_603plus_passthrough=false.
* Both suppress -- no Reason header at all.
*/
switch_call_cause_t cause;
switch_event_bind("test_603plus", SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE,
SWITCH_EVENT_SUBCLASS_ANY, on_hangup_complete, NULL);
reset_capture();
originate_and_wait("sofia/gateway/test/+15553336064", &cause);
fst_xcheck(cause == SWITCH_CAUSE_CALL_REJECTED, "Expected CALL_REJECTED for 603");
fst_xcheck(capture.received == SWITCH_TRUE, "Should have received outbound hangup event");
fst_xcheck(strcasecmp(capture.sip_invite_failure_phrase, "Network Blocked") != 0,
"disable_q850+passthrough=false must NOT send 'Network Blocked' phrase");
fst_xcheck(zstr_buf(capture.sip_reason),
"disable_q850+passthrough=false must suppress Reason header entirely");
switch_event_unbind_callback(on_hangup_complete);
}
FST_TEST_END()
FST_TEST_BEGIN(disable_reason_passthrough_default)
{
/*
* Extension +15553336065: disable_q850_reason=true, passthrough not set.
* disable_q850_reason suppresses everything, passthrough not set = no override.
* No Reason header, phrase is "Decline".
*/
switch_call_cause_t cause;
switch_event_bind("test_603plus", SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE,
SWITCH_EVENT_SUBCLASS_ANY, on_hangup_complete, NULL);
reset_capture();
originate_and_wait("sofia/gateway/test/+15553336065", &cause);
fst_xcheck(cause == SWITCH_CAUSE_CALL_REJECTED, "Expected CALL_REJECTED for 603");
fst_xcheck(capture.received == SWITCH_TRUE, "Should have received outbound hangup event");
fst_xcheck(strcasecmp(capture.sip_invite_failure_phrase, "Network Blocked") != 0,
"disable_q850+default must NOT send 'Network Blocked' phrase");
fst_xcheck(zstr_buf(capture.sip_reason),
"disable_q850+default must suppress Reason header");
switch_event_unbind_callback(on_hangup_complete);
}
FST_TEST_END()
}
FST_MODULE_END()
}
FST_CORE_END()
+232 -127
View File
@@ -41,7 +41,9 @@ SWITCH_MODULE_RUNTIME_FUNCTION(mod_verto_runtime);
SWITCH_MODULE_DEFINITION(mod_verto, mod_verto_load, mod_verto_shutdown, mod_verto_runtime);
#define HTTP_CHUNK_SIZE 1024 * 32
#define HTTP_POST_MAX_BODY (10 * 1024 * 1024) /* max accepted Content-Length for form-urlencoded POST */
#define EP_NAME "verto.rtc"
#define VERTO_SPEED_TEST_MAX_SIZE (10 * 1024 * 1024)
//#define WSS_STANDALONE 1
#include "libks/ks.h"
@@ -150,6 +152,114 @@ static void verto_deinit_ssl(verto_profile_t *profile)
}
}
static SSL_CTX *verto_create_ssl_ctx(verto_profile_t *profile, const char **errp)
{
SSL_CTX *ctx = SSL_CTX_new(profile->ssl_method);
if (!ctx) {
*errp = "Failed to create SSL context";
return NULL;
}
/* Disable SSLv2 */
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
/* Disable SSLv3 */
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
/* Disable TLSv1 */
SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
/* Disable Compression CRIME (Compression Ratio Info-leak Made Easy) */
SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION);
if (!zstr(profile->chain)) {
if (switch_file_exists(profile->chain, NULL) != SWITCH_STATUS_SUCCESS) {
*errp = "SUPPLIED CHAIN FILE NOT FOUND";
goto fail;
}
if (!SSL_CTX_use_certificate_chain_file(ctx, profile->chain)) {
*errp = "CERT CHAIN FILE ERROR";
goto fail;
}
}
if (switch_file_exists(profile->cert, NULL) != SWITCH_STATUS_SUCCESS) {
*errp = "SUPPLIED CERT FILE NOT FOUND";
goto fail;
}
if (!SSL_CTX_use_certificate_file(ctx, profile->cert, SSL_FILETYPE_PEM)) {
*errp = "CERT FILE ERROR";
goto fail;
}
if (switch_file_exists(profile->key, NULL) != SWITCH_STATUS_SUCCESS) {
*errp = "SUPPLIED KEY FILE NOT FOUND";
goto fail;
}
if (!SSL_CTX_use_PrivateKey_file(ctx, profile->key, SSL_FILETYPE_PEM)) {
*errp = "PRIVATE KEY FILE ERROR";
goto fail;
}
if (!SSL_CTX_check_private_key(ctx)) {
*errp = "PRIVATE KEY FILE ERROR";
goto fail;
}
SSL_CTX_set_cipher_list(ctx, "HIGH:!DSS:!aNULL@STRENGTH");
return ctx;
fail:
SSL_CTX_free(ctx);
return NULL;
}
static int verto_reload_ssl(verto_profile_t *profile)
{
const char *err = NULL;
SSL_CTX *new_ctx = verto_create_ssl_ctx(profile, &err);
if (!new_ctx) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SSL reload failed for profile %s: %s\n", profile->name, err);
return 0;
}
SSL_CTX_free(profile->ssl_ctx);
profile->ssl_ctx = new_ctx;
profile->ssl_ready = 1;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "SSL certificates reloaded for profile %s\n", profile->name);
return 1;
}
static void cert_reload_handler(switch_event_t *event)
{
verto_profile_t *p;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Certificate reload event received, processing\n");
switch_mutex_lock(verto_globals.mutex);
for (p = verto_globals.profile_head; p; p = p->next) {
if (p->running) {
switch_thread_rwlock_wrlock(p->rwlock);
verto_reload_ssl(p);
switch_thread_rwlock_unlock(p->rwlock);
}
}
switch_mutex_unlock(verto_globals.mutex);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Certificate reload event processed\n");
}
static void close_file(ks_socket_t *sock)
{
if (*sock != KS_SOCK_INVALID) {
@@ -174,84 +284,30 @@ void verto_broadcast(const char *event_channel, cJSON *json, const char *key, sw
static int verto_init_ssl(verto_profile_t *profile)
{
const char *err = "";
const char *err = NULL;
int i = 0;
profile->ssl_method = SSLv23_server_method(); /* create server instance */
profile->ssl_ctx = SSL_CTX_new(profile->ssl_method); /* create context */
profile->ssl_ready = 1;
assert(profile->ssl_ctx);
profile->ssl_method = SSLv23_server_method();
profile->ssl_ctx = verto_create_ssl_ctx(profile, &err);
/* Disable SSLv2 */
SSL_CTX_set_options(profile->ssl_ctx, SSL_OP_NO_SSLv2);
/* Disable SSLv3 */
SSL_CTX_set_options(profile->ssl_ctx, SSL_OP_NO_SSLv3);
/* Disable TLSv1 */
SSL_CTX_set_options(profile->ssl_ctx, SSL_OP_NO_TLSv1);
/* Disable Compression CRIME (Compression Ratio Info-leak Made Easy) */
SSL_CTX_set_options(profile->ssl_ctx, SSL_OP_NO_COMPRESSION);
/* set the local certificate from CertFile */
if (!zstr(profile->chain)) {
if (switch_file_exists(profile->chain, NULL) != SWITCH_STATUS_SUCCESS) {
err = "SUPPLIED CHAIN FILE NOT FOUND\n";
goto fail;
}
if (!SSL_CTX_use_certificate_chain_file(profile->ssl_ctx, profile->chain)) {
err = "CERT CHAIN FILE ERROR";
goto fail;
}
}
if (switch_file_exists(profile->cert, NULL) != SWITCH_STATUS_SUCCESS) {
err = "SUPPLIED CERT FILE NOT FOUND\n";
goto fail;
}
if (!SSL_CTX_use_certificate_file(profile->ssl_ctx, profile->cert, SSL_FILETYPE_PEM)) {
err = "CERT FILE ERROR";
goto fail;
}
/* set the private key from KeyFile */
if (switch_file_exists(profile->key, NULL) != SWITCH_STATUS_SUCCESS) {
err = "SUPPLIED KEY FILE NOT FOUND\n";
goto fail;
}
if (!SSL_CTX_use_PrivateKey_file(profile->ssl_ctx, profile->key, SSL_FILETYPE_PEM)) {
err = "PRIVATE KEY FILE ERROR";
goto fail;
}
/* verify private key */
if ( !SSL_CTX_check_private_key(profile->ssl_ctx) ) {
err = "PRIVATE KEY FILE ERROR";
goto fail;
}
SSL_CTX_set_cipher_list(profile->ssl_ctx, "HIGH:!DSS:!aNULL@STRENGTH");
return 1;
fail:
if (!profile->ssl_ctx) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SSL ERR: %s\n", err);
profile->ssl_ready = 0;
verto_deinit_ssl(profile);
for (i = 0; i < profile->i; i++) {
if (profile->ip[i].secure) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SSL NOT ENABLED FOR LISTENER %s:%d. REVERTING TO WS\n",
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "SSL NOT READY FOR LISTENER %s:%d. USE reloadcert AFTER FIXING CERTIFICATES\n",
profile->ip[i].local_ip, profile->ip[i].local_port);
profile->ip[i].secure = 0;
}
}
return 0;
}
profile->ssl_ready = 1;
return 1;
}
@@ -1031,23 +1087,6 @@ static switch_bool_t check_auth(jsock_t *jsock, cJSON *params, int *code, char *
}
}
if ((json_ptr = cJSON_GetObjectItem(params, "userVariables"))) {
cJSON * i;
switch_mutex_lock(jsock->flag_mutex);
for(i = json_ptr->child; i; i = i->next) {
if (i->type == cJSON_True) {
switch_event_add_header_string(jsock->user_vars, SWITCH_STACK_BOTTOM, i->string, "true");
} else if (i->type == cJSON_False) {
switch_event_add_header_string(jsock->user_vars, SWITCH_STACK_BOTTOM, i->string, "false");
} else if (!zstr(i->string) && !zstr(i->valuestring)) {
switch_event_add_header_string(jsock->user_vars, SWITCH_STACK_BOTTOM, i->string, i->valuestring);
}
}
switch_mutex_unlock(jsock->flag_mutex);
}
if (jsock->profile->send_passwd || verto_globals.send_passwd) {
switch_event_add_header_string(req_params, SWITCH_STACK_BOTTOM, "user_supplied_pass", passwd);
}
@@ -1064,20 +1103,8 @@ static switch_bool_t check_auth(jsock_t *jsock, cJSON *params, int *code, char *
const char *use_passwd = NULL, *verto_context = NULL, *verto_dialplan = NULL;
time_t now = switch_epoch_time_now(NULL);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Login sucessful for user: %s domain: %s\n", id, domain);
jsock->logintime = now;
jsock->id = switch_core_strdup(jsock->pool, id);
jsock->domain = switch_core_strdup(jsock->pool, domain);
jsock->uid = switch_core_sprintf(jsock->pool, "%s@%s", id, domain);
jsock->ready = 1;
if (!x_user) {
switch_event_destroy(&req_params);
r = SWITCH_TRUE;
goto end;
}
/* Pre-scan <user><params>: extract credentials and verto-context/dialplan
* into locals only. No jsock writes here. */
if ((x_params = switch_xml_child(x_user, "params"))) {
for (x_param = switch_xml_child(x_params, "param"); x_param; x_param = x_param->next) {
const char *var = switch_xml_attr_soft(x_param, "name");
@@ -1099,8 +1126,63 @@ static switch_bool_t check_auth(jsock_t *jsock, cJSON *params, int *code, char *
} else if (!strcasecmp(var, "verto-dialplan")) {
verto_dialplan = val;
}
}
}
switch_event_add_header_string(jsock->params, SWITCH_STACK_BOTTOM, var, val);
/* Password gate. blind_reg with no x_user passes by config. */
if (x_user && (zstr(use_passwd) || strcmp(a1_hash ? a1_hash : passwd, use_passwd))) {
*code = CODE_AUTH_FAILED;
switch_snprintf(message, mlen, "Authentication Failure");
login_fire_custom_event(jsock, params, 0, "Authentication Failure");
switch_xml_clear_user_cache("id", id, domain);
switch_xml_free(x_user);
switch_event_destroy(&req_params);
goto end;
}
/* Commit jsock state — reachable only post-gate. */
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Login successful for user: %s domain: %s\n", id, domain);
jsock->logintime = now;
jsock->id = switch_core_strdup(jsock->pool, id);
jsock->domain = switch_core_strdup(jsock->pool, domain);
jsock->uid = switch_core_sprintf(jsock->pool, "%s@%s", id, domain);
if ((json_ptr = cJSON_GetObjectItem(params, "userVariables"))) {
cJSON *i;
switch_mutex_lock(jsock->flag_mutex);
for (i = json_ptr->child; i; i = i->next) {
if (i->type == cJSON_True) {
switch_event_add_header_string(jsock->user_vars, SWITCH_STACK_BOTTOM, i->string, "true");
} else if (i->type == cJSON_False) {
switch_event_add_header_string(jsock->user_vars, SWITCH_STACK_BOTTOM, i->string, "false");
} else if (!zstr(i->string) && !zstr(i->valuestring)) {
switch_event_add_header_string(jsock->user_vars, SWITCH_STACK_BOTTOM, i->string, i->valuestring);
}
}
switch_mutex_unlock(jsock->flag_mutex);
}
/* blind_reg path: no XML user located — jsock state already committed above;
* skip directory persistence (params/variables/dialplan/context) and return. */
if (!x_user) {
switch_event_destroy(&req_params);
/* ready=1 is the last state write so cross-thread readers that
* gate on `ready && !zstr(uid)` see a fully populated jsock. */
jsock->ready = 1;
r = SWITCH_TRUE;
goto end;
}
/* Second pass over <user><params>: persist every entry into jsock->params.
* Pre-scan above only read credentials/verto-context/dialplan into locals.
* Must run post-gate these headers feed channel variables on later calls. */
if ((x_params = switch_xml_child(x_user, "params"))) {
for (x_param = switch_xml_child(x_params, "param"); x_param; x_param = x_param->next) {
switch_event_add_header_string(jsock->params, SWITCH_STACK_BOTTOM,
switch_xml_attr_soft(x_param, "name"),
switch_xml_attr_soft(x_param, "value"));
}
}
@@ -1138,21 +1220,12 @@ static switch_bool_t check_auth(jsock_t *jsock, cJSON *params, int *code, char *
jsock->context = switch_core_strdup(jsock->pool, verto_context);
}
if (!use_passwd || zstr(use_passwd) || strcmp(a1_hash ? a1_hash : passwd, use_passwd)) {
r = SWITCH_FALSE;
*code = CODE_AUTH_FAILED;
switch_snprintf(message, mlen, "Authentication Failure");
jsock->uid = NULL;
login_fire_custom_event(jsock, params, 0, "Authentication Failure");
switch_xml_clear_user_cache("id", id, domain);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,"auth using %s\n",a1_hash ? "a1-hash" : "username & password");
r = SWITCH_TRUE;
check_permissions(jsock, x_user, params);
}
/* ready=1 is the last state write so cross-thread readers that
* gate on `ready && !zstr(uid)` see a fully populated jsock. */
jsock->ready = 1;
r = SWITCH_TRUE;
switch_xml_free(x_user);
}
@@ -1216,10 +1289,11 @@ static jsock_t *get_jsock(const char *uuid)
static void tech_reattach(verto_pvt_t *tech_pvt, jsock_t *jsock);
static void attach_jsock(jsock_t *jsock)
static switch_bool_t attach_jsock(jsock_t *jsock)
{
jsock_t *jp;
int proceed = 1;
switch_bool_t result = SWITCH_TRUE;
switch_mutex_lock(verto_globals.jsock_mutex);
@@ -1228,6 +1302,17 @@ static void attach_jsock(jsock_t *jsock)
if ((jp = switch_core_hash_find(verto_globals.jsock_hash, jsock->uuid_str))) {
if (jp == jsock) {
proceed = 0;
} else if (!zstr(jp->uid) && !zstr(jsock->uid) && strcmp(jp->uid, jsock->uid)) {
/* Refuse cross-identity takeover when both jsocks are authenticated under different uids.
* Clear uuid_str and set nodelete to prevent any uuid_str-keyed teardown
* (detach_jsock, del_jsock, detach_calls) from touching jp. */
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"User %s blocked from taking over session %s owned by %s\n",
jsock->uid, jsock->uuid_str, jp->uid);
jsock->nodelete = 1;
jsock->uuid_str[0] = '\0';
proceed = 0;
result = SWITCH_FALSE;
} else {
cJSON *params = NULL;
cJSON *msg = NULL;
@@ -1248,6 +1333,7 @@ static void attach_jsock(jsock_t *jsock)
}
switch_mutex_unlock(verto_globals.jsock_mutex);
return result;
}
static void detach_jsock(jsock_t *jsock)
@@ -1426,10 +1512,8 @@ static void process_jrpc_response(jsock_t *jsock, cJSON *json)
{
}
static void set_session_id(jsock_t *jsock, const char *uuid)
static switch_bool_t set_session_id(jsock_t *jsock, const char *uuid)
{
//cJSON *params, *msg = jrpc_new(0);
if (!zstr(uuid)) {
switch_set_string(jsock->uuid_str, uuid);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s re-connecting session %s\n", jsock->name, jsock->uuid_str);
@@ -1438,8 +1522,7 @@ static void set_session_id(jsock_t *jsock, const char *uuid)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s new RPC session %s\n", jsock->name, jsock->uuid_str);
}
attach_jsock(jsock);
return attach_jsock(jsock);
}
static cJSON *process_jrpc(jsock_t *jsock, cJSON *json)
@@ -1459,11 +1542,6 @@ static cJSON *process_jrpc(jsock_t *jsock, cJSON *json)
sessid = cJSON_GetObjectCstr(params, "sessid");
}
if (!switch_test_flag(jsock, JPFLAG_INIT)) {
set_session_id(jsock, sessid);
switch_set_flag(jsock, JPFLAG_INIT);
}
if (zstr(version) || strcmp(version, "2.0")) {
reply = jrpc_new(0);
jrpc_add_error(reply, CODE_INVALID, "Invalid message", id);
@@ -1490,6 +1568,17 @@ static cJSON *process_jrpc(jsock_t *jsock, cJSON *json)
switch_set_flag(jsock, JPFLAG_AUTHED);
}
/* Bind only after the auth gate — attach_jsock()'s eviction
* must not be reachable pre-auth. */
if (!switch_test_flag(jsock, JPFLAG_INIT)) {
if (!set_session_id(jsock, sessid)) {
jrpc_add_error(reply, CODE_AUTH_FAILED, "Session in use", id);
jsock->drop = 1;
goto end;
}
switch_set_flag(jsock, JPFLAG_INIT);
}
if (!method || !(func = jrpc_get_func(jsock, method))) {
jrpc_add_error(reply, -32601, "Invalid Method, Missing Method or Permission Denied", id);
} else {
@@ -1770,7 +1859,7 @@ new_req:
char *buffer = NULL;
switch_ssize_t len = 0, bytes = 0;
if (request->content_length && request->content_length > 10 * 1024 * 1024 - 1) {
if (request->content_length && request->content_length >= HTTP_POST_MAX_BODY) {
char *data = "HTTP/1.1 413 Request Entity Too Large\r\n"
"Content-Length: 0\r\n\r\n";
kws_raw_write(jsock->ws, data, strlen(data));
@@ -1778,16 +1867,16 @@ new_req:
goto done;
}
if (!(buffer = malloc(2 * 1024 * 1024))) {
if (!(buffer = malloc(request->content_length + 1))) {
goto request_err;
}
while(bytes < (switch_ssize_t)request->content_length) {
len = request->content_length - bytes;
#define WS_BLOCK 1
#define WS_BLOCK 10000 /* ms; matches libks's internal default */
if ((len = kws_raw_read(jsock->ws, buffer + bytes, len, WS_BLOCK)) < 0) {
if ((len = kws_raw_read(jsock->ws, buffer + bytes, len, WS_BLOCK)) <= 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Read error %" SWITCH_SSIZE_T_FMT"\n", len);
goto done;
}
@@ -1972,6 +2061,7 @@ static void client_run(jsock_t *jsock)
ks_pool_open(&jsock->kpool);
switch_thread_rwlock_rdlock(jsock->profile->rwlock);
#if defined(KS_VERSION_NUM) && KS_VERSION_NUM >= 20000
params = ks_json_create_object();
ks_json_add_number_to_object(params, "payload_size_max", 1000000);
@@ -1979,8 +2069,10 @@ static void client_run(jsock_t *jsock)
#else
if (kws_init(&jsock->ws, jsock->client_socket, (jsock->ptype & PTYPE_CLIENT_SSL) ? jsock->profile->ssl_ctx : NULL, 0, flags, jsock->kpool) != KS_STATUS_SUCCESS) {
#endif
switch_thread_rwlock_unlock(jsock->profile->rwlock);
log_and_exit(SWITCH_LOG_NOTICE, "%s WS SETUP FAILED\n", jsock->name);
}
switch_thread_rwlock_unlock(jsock->profile->rwlock);
if (kws_test_flag(jsock->ws, KWS_HTTP)) {
http_run(jsock);
@@ -2054,16 +2146,26 @@ static void client_run(jsock_t *jsock)
char repl[2048] = "";
switch_time_t a, b;
if (!switch_test_flag(jsock, JPFLAG_AUTHED)) {
die("%s Speed-test request before authentication\n", jsock->name);
}
if (bytes < 4) {
continue;
}
if (s[1] == 'S' && s[2] == 'P') {
if (s[3] == 'U') {
int i, size = 0;
int i;
long size;
char *p = s+4;
int loops = 0;
int rem = 0;
int dur = 0, j = 0;
if ((size = atoi(p)) <= 0) {
size = strtol(p, NULL, 10);
if (size <= 0 || size > VERTO_SPEED_TEST_MAX_SIZE) {
continue;
}
@@ -2071,7 +2173,7 @@ static void client_run(jsock_t *jsock)
do {
bytes = kws_read_frame(jsock->ws, &oc, &data);
s = (char *) data;
} while (bytes && data && s[0] == '#' && s[3] == 'B');
} while (bytes >= 4 && data && s[0] == '#' && s[3] == 'B');
b = switch_time_now();
if (!bytes || !data) continue;
@@ -4690,7 +4792,7 @@ static int start_jsock(verto_profile_t *profile, ks_socket_t sock, int family)
for (i = 0; i < profile->i; i++) {
if ( profile->server_socket[i] == sock ) {
if (profile->ip[i].secure) {
if (profile->ip[i].secure && profile->ssl_ready) {
ptype = PTYPE_CLIENT_SSL;
}
break;
@@ -6887,6 +6989,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_verto_load)
switch_core_register_secondary_recover_callback(modname, verto_recover_callback);
switch_event_bind(modname, SWITCH_EVENT_CERT_RELOAD, SWITCH_EVENT_SUBCLASS_ANY, cert_reload_handler, NULL);
if (verto_globals.enable_presence) {
switch_event_bind(modname, SWITCH_EVENT_CHANNEL_CALLSTATE, SWITCH_EVENT_SUBCLASS_ANY, presence_event_handler, NULL);
}
@@ -6922,6 +7026,7 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_verto_shutdown)
switch_core_hash_destroy(&json_GLOBALS.store_hash);
switch_event_channel_unbind(NULL, verto_broadcast, NULL);
switch_event_unbind_callback(cert_reload_handler);
switch_event_unbind_callback(presence_event_handler);
switch_event_unbind_callback(event_handler);
@@ -64,6 +64,7 @@ void ei_link(listener_t *listener, erlang_pid * from, erlang_pid * to)
char msgbuf[2048];
char *s;
int index = 0;
switch_size_t send_len;
int status = SWITCH_STATUS_SUCCESS;
switch_socket_t *sock = NULL;
switch_os_sock_put(&sock, &listener->sockdes, listener->pool);
@@ -82,7 +83,8 @@ void ei_link(listener_t *listener, erlang_pid * from, erlang_pid * to)
/* sum: 542 */
switch_mutex_lock(listener->sock_mutex);
status = switch_socket_send(sock, msgbuf, (switch_size_t *) &index);
send_len = (switch_size_t)index;
status = switch_socket_send(sock, msgbuf, &send_len);
if (status != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to link to process on %s\n", listener->peer_nodename);
}
@@ -283,8 +285,7 @@ int ei_sendto(ei_cnode * ec, int fd, struct erlang_process *process, ei_x_buff *
/* convert an erlang reference to some kind of hashed string so we can store it as a hash key */
void ei_hash_ref(erlang_ref * ref, char *output)
{
/* very lazy */
sprintf(output, "%d.%d.%d@%s", ref->n[0], ref->n[1], ref->n[2], ref->node);
snprintf(output, EI_HASH_REF_LEN, "%d.%d.%d@%s", ref->n[0], ref->n[1], ref->n[2], ref->node);
}
@@ -91,7 +91,7 @@ api(Node, Cmd) ->
%% sent to calling process after it is received. This function
%% returns the result of the initial bgapi call or `timeout' if FreeSWITCH fails
%% to respond.
-spec(bgapi/3 :: (Node :: atom(), Cmd :: atom(), Args :: string()) -> {'ok', string()} | {'error', any()} | 'timeout').
-spec bgapi(Node :: atom(), Cmd :: atom(), Args :: string()) -> {'ok', string()} | {'error', any()} | 'timeout'.
bgapi(Node, Cmd, Args) ->
Self = self(),
% spawn a new process so that both responses go here instead of directly to
@@ -128,7 +128,7 @@ bgapi(Node, Cmd, Args) ->
%% passed as the argument to `Fun' after it is received. This function
%% returns the result of the initial bgapi call or `timeout' if FreeSWITCH fails
%% to respond.
-spec(bgapi/4 :: (Node :: atom(), Cmd :: atom(), Args :: string(), Fun :: fun()) -> 'ok' | {'error', any()} | 'timeout').
-spec bgapi(Node :: atom(), Cmd :: atom(), Args :: string(), Fun :: fun()) -> 'ok' | {'error', any()} | 'timeout'.
bgapi(Node, Cmd, Args, Fun) ->
Self = self(),
% spawn a new process so that both responses go here instead of directly to
@@ -804,11 +804,11 @@ static switch_status_t handle_msg_sendevent(listener_t *listener, int arity, ei_
} else {
switch_event_types_t etype;
if (switch_name_event(ename, &etype) == SWITCH_STATUS_SUCCESS) {
switch_event_t *event;
switch_event_t *event = NULL;
if ((strlen(esname) && switch_event_create_subclass(&event, etype, esname) == SWITCH_STATUS_SUCCESS) ||
switch_event_create(&event, etype) == SWITCH_STATUS_SUCCESS) {
char key[1024];
char *value;
char *value = NULL;
int type;
int size;
int i = 0;
@@ -828,14 +828,15 @@ static switch_status_t handle_msg_sendevent(listener_t *listener, int arity, ei_
value = malloc(size + 1);
if (ei_decode_string(buf->buff, &buf->index, value)) {
switch_safe_free(value);
fail = SWITCH_TRUE;
break;
}
if (!fail && !strcmp(key, "body")) {
if (!strcmp(key, "body")) {
switch_safe_free(event->body);
event->body = value;
} else if (!fail) {
} else {
switch_event_add_header_string_nodup(event, SWITCH_STACK_BOTTOM, key, value);
}
@@ -896,14 +897,13 @@ static switch_status_t handle_msg_sendmsg(listener_t *listener, int arity, ei_x_
value = malloc(size + 1);
if (ei_decode_string(buf->buff, &buf->index, value)) {
switch_safe_free(value);
fail = SWITCH_TRUE;
break;
}
if (!fail) {
switch_event_add_header_string_nodup(event, SWITCH_STACK_BOTTOM, key, value);
}
}
if (headerlength != i || fail) {
ei_x_encode_tuple_header(rbuf, 2);
@@ -1204,7 +1204,7 @@ static switch_status_t handle_ref_tuple(listener_t *listener, erlang_msg * msg,
{
erlang_ref ref;
erlang_pid pid;
char hash[100];
char hash[EI_HASH_REF_LEN];
int arity;
const void *key;
void *val;
@@ -1232,7 +1232,7 @@ static switch_status_t handle_ref_tuple(listener_t *listener, erlang_msg * msg,
for (iter = switch_core_hash_first(listener->sessions); iter; iter = switch_core_hash_next(&iter)) {
switch_core_hash_this(iter, &key, NULL, &val);
se = (session_elem_t*)val;
if (switch_test_flag(se, LFLAG_WAITING_FOR_PID) && se->spawn_reply && !strncmp(se->spawn_reply->hash, hash, 100)) {
if (switch_test_flag(se, LFLAG_WAITING_FOR_PID) && se->spawn_reply && !strncmp(se->spawn_reply->hash, hash, EI_HASH_REF_LEN)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "found matching session for %s : %s\n", hash, se->uuid_str);
@@ -1376,6 +1376,7 @@ int handle_msg(listener_t *listener, erlang_msg * msg, ei_x_buff * buf, ei_x_buf
break;
case ERL_REFERENCE_EXT:
case ERL_NEW_REFERENCE_EXT:
case ERL_NEWER_REFERENCE_EXT:
ret = handle_ref_tuple(listener, msg, buf, rbuf);
break;
default:
@@ -968,9 +968,10 @@ static void handle_exit(listener_t *listener, erlang_pid * pid)
static void listener_main_loop(listener_t *listener)
{
int status = 1;
int recv_erl_errno = ETIMEDOUT;
int msgs_sent = 0; /* how many messages we sent in a loop */
while ((status >= 0 || erl_errno == ETIMEDOUT || erl_errno == EAGAIN) && !prefs.done) {
while ((status >= 0 || recv_erl_errno == ETIMEDOUT || recv_erl_errno == EAGAIN) && !prefs.done) {
erlang_msg msg;
ei_x_buff buf;
ei_x_buff rbuf;
@@ -983,6 +984,9 @@ static void listener_main_loop(listener_t *listener)
/* do we need the mutex when reading? */
/*switch_mutex_lock(listener->sock_mutex); */
status = ei_xreceive_msg_tmo(listener->sockdes, &msg, &buf, 1);
/* snapshot erl_errno before any outbound ei call (queue flushers below)
clobbers this thread-local slot. */
recv_erl_errno = erl_errno;
/*switch_mutex_unlock(listener->sock_mutex); */
switch (status) {
@@ -1001,6 +1005,8 @@ static void listener_main_loop(listener_t *listener)
if (handle_msg(listener, &msg, &buf, &rbuf)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "handle_msg requested exit\n");
ei_x_free(&buf);
ei_x_free(&rbuf);
return;
}
break;
@@ -1016,6 +1022,8 @@ static void listener_main_loop(listener_t *listener)
if (handle_msg(listener, &msg, &buf, &rbuf)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "handle_msg requested exit\n");
ei_x_free(&buf);
ei_x_free(&rbuf);
return;
}
break;
@@ -1026,6 +1034,7 @@ static void listener_main_loop(listener_t *listener)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "erl_unlink\n");
break;
case ERL_EXIT:
case ERL_EXIT2:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "erl_exit from %s <%d.%d.%d>\n", msg.from.node, msg.from.creation, msg.from.num,
msg.from.serial);
@@ -1037,8 +1046,8 @@ static void listener_main_loop(listener_t *listener)
}
break;
case ERL_ERROR:
if (erl_errno != ETIMEDOUT && erl_errno != EAGAIN) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "erl_error: status=%d, erl_errno=%d errno=%d\n", status, erl_errno, errno);
if (recv_erl_errno != ETIMEDOUT && recv_erl_errno != EAGAIN) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "erl_error: status=%d, erl_errno=%d errno=%d\n", status, recv_erl_errno, errno);
}
break;
default:
@@ -1069,7 +1078,7 @@ static void listener_main_loop(listener_t *listener)
if (prefs.done) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "shutting down listener\n");
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "listener exit: status=%d, erl_errno=%d errno=%d\n", status, erl_errno, errno);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "listener exit: status=%d, erl_errno=%d errno=%d\n", status, recv_erl_errno, errno);
}
}
@@ -1513,7 +1522,7 @@ session_elem_t *attach_call_to_spawned_process(listener_t *listener, char *modul
{
/* create a session list element */
session_elem_t *session_element = session_elem_create(listener, session);
char hash[100];
char hash[EI_HASH_REF_LEN];
spawn_reply_t *p;
erlang_ref ref;
@@ -1720,8 +1729,6 @@ SWITCH_STANDARD_APP(erlang_sendmsg_function)
ei_x_buff buf;
listener_t *listener;
ei_x_new_with_version(&buf);
/* process app arguments */
if (data && (mydata = switch_core_session_strdup(session, data))) {
argc = switch_separate_string(mydata, ' ', argv, 3);
@@ -1737,6 +1744,7 @@ SWITCH_STANDARD_APP(erlang_sendmsg_function)
/*switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "sendmsg: {%s, %s} ! %s\n", reg_name, node, argv[2]); */
ei_x_new_with_version(&buf);
ei_x_encode_tuple_header(&buf, 2);
ei_x_encode_atom(&buf, "freeswitch_sendmsg");
_ei_x_encode_string(&buf, argv[2]);
@@ -1754,6 +1762,8 @@ SWITCH_STANDARD_APP(erlang_sendmsg_function)
switch_thread_rwlock_unlock(listener->rwlock);
}
ei_x_free(&buf);
}
@@ -238,6 +238,7 @@ extern prefs_t prefs;
int handle_msg(listener_t *listener, erlang_msg * msg, ei_x_buff * buf, ei_x_buff * rbuf);
/* ei_helpers.c */
#define EI_HASH_REF_LEN (MAXATOMLEN_UTF8 + 64)
void ei_link(listener_t *listener, erlang_pid * from, erlang_pid * to);
void ei_encode_switch_event_headers(ei_x_buff * ebuf, switch_event_t *event);
void ei_encode_switch_event_tag(ei_x_buff * ebuf, switch_event_t *event, char *tag);
@@ -1117,7 +1117,7 @@ SWITCH_STANDARD_API(event_sink_function)
}
if (listener->format == EVENT_FORMAT_JSON) {
char *p = "{}";
char *p;
cJSON_AddItemToObject(cj, "events", cjevents);
p = cJSON_Print(cj);
if (cj && p) stream->write_function(stream, p);
+1 -1
View File
@@ -1289,7 +1289,7 @@ void do_telecast(switch_stream_handle_t *stream)
char *path_info = switch_event_get_header(stream->param_event, "http-path-info");
char *uuid = strdup(path_info + 4);
switch_core_session_t *tsession;
char *fname = "stream.mp3";
char *fname;
switch_assert(uuid);
if ((fname = strchr(uuid, '/'))) {
@@ -39455,6 +39455,7 @@ public enum switch_event_types_t {
SWITCH_EVENT_DEVICE_STATE,
SWITCH_EVENT_TEXT,
SWITCH_EVENT_SHUTDOWN_REQUESTED,
SWITCH_EVENT_CERT_RELOAD,
SWITCH_EVENT_ALL
}
@@ -293,7 +293,7 @@ switch_status_t Session::run_dtmf_callback(void *input, switch_input_type_t ityp
PyObject *pyresult, *arglist, *io = NULL;
int ts = 0;
char *str = NULL, *what = (char*)"";
char *str = NULL, *what;
if (TS) {
ts++;
+1 -1
View File
@@ -424,7 +424,7 @@ static void v8_error(Isolate* isolate, TryCatch* try_catch)
String::Utf8Value exception(try_catch->Exception());
const char *exception_string = js_safe_str(*exception);
Handle<Message> message = try_catch->Message();
const char *msg = "";
const char *msg;
string filename = __FILE__;
int line = __LINE__;
string text = "";
+14 -5
View File
@@ -4549,7 +4549,7 @@ static void restore_pmaps(switch_rtp_engine_t *engine)
static const char *media_flow_varname(switch_media_type_t type)
{
const char *varname = "invalid";
const char *varname;
switch(type) {
case SWITCH_MEDIA_TYPE_AUDIO:
@@ -4561,6 +4561,9 @@ static const char *media_flow_varname(switch_media_type_t type)
case SWITCH_MEDIA_TYPE_TEXT:
varname = "text_media_flow";
break;
default:
varname = "invalid";
break;
}
return varname;
@@ -4568,7 +4571,7 @@ static const char *media_flow_varname(switch_media_type_t type)
static const char *remote_media_flow_varname(switch_media_type_t type)
{
const char *varname = "invalid";
const char *varname;
switch(type) {
case SWITCH_MEDIA_TYPE_AUDIO:
@@ -4580,6 +4583,9 @@ static const char *remote_media_flow_varname(switch_media_type_t type)
case SWITCH_MEDIA_TYPE_TEXT:
varname = "remote_text_media_flow";
break;
default:
varname = "invalid";
break;
}
return varname;
@@ -4587,7 +4593,7 @@ static const char *remote_media_flow_varname(switch_media_type_t type)
static void media_flow_get_mode(switch_media_flow_t smode, const char **mode_str, switch_media_flow_t *opp_mode)
{
const char *smode_str = "";
const char *smode_str;
switch_media_flow_t opp_smode = smode;
switch(smode) {
@@ -4608,6 +4614,9 @@ static void media_flow_get_mode(switch_media_flow_t smode, const char **mode_str
case SWITCH_MEDIA_FLOW_SENDRECV:
smode_str = "sendrecv";
break;
default:
smode_str = "";
break;
}
*mode_str = smode_str;
@@ -11775,7 +11784,7 @@ SWITCH_DECLARE(void) switch_core_media_set_udptl_image_sdp(switch_core_session_t
char max_data[128] = "";
const char *ip;
uint32_t port;
const char *family = "IP4";
const char *family;
const char *username;
const char *bit_removal_on = "a=T38FaxFillBitRemoval\r\n";
const char *bit_removal_off = "";
@@ -12033,7 +12042,7 @@ SWITCH_DECLARE(void) switch_core_media_patch_sdp(switch_core_session_t *session)
switch_size_t len;
if (oe) {
const char *family = "IP4";
const char *family;
char o_line[1024] = "";
if (oe >= pe) {
+2 -1
View File
@@ -1932,7 +1932,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_thread_pool_launch(switch_co
} else {
switch_set_flag(session, SSF_THREAD_RUNNING);
switch_set_flag(session, SSF_THREAD_STARTED);
td = switch_core_session_alloc(session, sizeof(*td));
switch_zmalloc(td, sizeof(*td));
td->alloc = 1;
td->obj = session;
td->func = switch_core_session_thread;
status = switch_queue_push(session_manager.thread_queue, td);
+1
View File
@@ -227,6 +227,7 @@ static char *EVENT_NAMES[] = {
"DEVICE_STATE",
"TEXT",
"SHUTDOWN_REQUESTED",
"CERT_RELOAD",
"ALL"
};
+1 -1
View File
@@ -114,7 +114,7 @@ static void msrp_deinit_ssl(void)
static void msrp_init_ssl(void)
{
const char *err = "";
const char *err;
globals.ssl_client_method = SSLv23_client_method();
globals.ssl_client_ctx = SSL_CTX_new(globals.ssl_client_method);
+1 -1
View File
@@ -3822,7 +3822,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_add_dtls(switch_rtp_t *rtp_session, d
switch_dtls_t *dtls;
const char *var;
int ret;
const char *kind = "";
const char *kind;
unsigned long ssl_method_error = 0;
unsigned long ssl_ctx_error = 0;
const SSL_METHOD *ssl_method;
+19 -33
View File
@@ -485,31 +485,24 @@ SWITCH_DECLARE(switch_stun_packet_t *) switch_stun_packet_build_header(switch_st
SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_add_binded_address(switch_stun_packet_t *packet, char *ipstr, uint16_t port, int family)
{
switch_stun_packet_attribute_t *attribute;
switch_stun_ip_t *ip;
attribute = (switch_stun_packet_attribute_t *) ((uint8_t *) & packet->first_attribute + ntohs(packet->header.length));
attribute->type = htons(SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS);
if (family == AF_INET6) {
switch_stun_ipv6_t *ipv6 = (switch_stun_ipv6_t *) attribute->value;
attribute->length = htons(20);
ipv6->family = 2;
ipv6->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
inet_pton(AF_INET6, ipstr, ipv6->address);
} else {
switch_stun_ip_t *ip = (switch_stun_ip_t *) attribute->value;
attribute->length = htons(8);
}
ip = (switch_stun_ip_t *) attribute->value;
ip->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
if (family == AF_INET6) {
ip->family = 2;
} else {
ip->family = 1;
}
if (family == AF_INET6) {
inet_pton(AF_INET6, ipstr, (struct in6_addr *) &ip->address);
} else {
inet_pton(AF_INET, ipstr, (int *) &ip->address);
ip->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
inet_pton(AF_INET, ipstr, &ip->address);
}
packet->header.length += htons(sizeof(switch_stun_packet_attribute_t)) + attribute->length;
@@ -519,32 +512,25 @@ SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_add_binded_address(switch_s
SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_add_xor_binded_address(switch_stun_packet_t *packet, char *ipstr, uint16_t port, int family)
{
switch_stun_packet_attribute_t *attribute;
switch_stun_ip_t *ip;
attribute = (switch_stun_packet_attribute_t *) ((uint8_t *) & packet->first_attribute + ntohs(packet->header.length));
attribute->type = htons(SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS);
if (family == AF_INET6) {
switch_stun_ipv6_t *ipv6 = (switch_stun_ipv6_t *) attribute->value;
attribute->length = htons(20);
ipv6->family = 2;
ipv6->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
inet_pton(AF_INET6, ipstr, ipv6->address);
v6_xor(ipv6->address, (uint8_t *)packet->header.id);
} else {
switch_stun_ip_t *ip = (switch_stun_ip_t *) attribute->value;
attribute->length = htons(8);
}
ip = (switch_stun_ip_t *) attribute->value;
ip->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
if (family == AF_INET6) {
ip->family = 2;
} else {
ip->family = 1;
}
if (family == AF_INET6) {
inet_pton(AF_INET6, ipstr, (struct in6_addr *) &ip->address);
v6_xor((uint8_t *)&ip->address, (uint8_t *)packet->header.id);
} else {
inet_pton(AF_INET, ipstr, (int *) &ip->address);
ip->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
inet_pton(AF_INET, ipstr, &ip->address);
ip->address = htonl(ntohl(ip->address) ^ STUN_MAGIC_COOKIE);
}
+2 -1
View File
@@ -4270,7 +4270,8 @@ switch_status_t clean_uri(char *uri)
argc = switch_separate_string(uri, '/', argv, sizeof(argv) / sizeof(argv[0]));
if (argc == sizeof(argv)) { /* too deep */
/* Intentionally using == instead of > because this way we would know that the url was fully parsed for sure */
if (argc == (sizeof(argv) / sizeof(argv[0]))) { /* too deep */
return SWITCH_STATUS_FALSE;
}
+2
View File
@@ -3,8 +3,10 @@ include $(top_srcdir)/build/modmake.rulesam
noinst_PROGRAMS = switch_event switch_hash switch_ivr_originate switch_utils switch_core switch_console switch_vpx switch_core_file \
switch_ivr_play_say switch_core_codec switch_rtp switch_xml
noinst_PROGRAMS += switch_core_video switch_core_db switch_vad switch_packetizer switch_core_session test_sofia switch_ivr_async switch_core_asr switch_log
noinst_PROGRAMS += switch_stun
noinst_PROGRAMS += test_tts_format
noinst_PROGRAMS+= switch_hold switch_sip
noinst_PROGRAMS += test_mod_verto
if HAVE_PCAP
noinst_PROGRAMS += switch_rtp_pcap
+73
View File
@@ -0,0 +1,73 @@
<?xml version="1.0"?>
<document type="freeswitch/xml">
<X-PRE-PROCESS cmd="exec-set" data="test=echo 1234"/>
<X-PRE-PROCESS cmd="set" data="default_password=$${test}"/>
<X-PRE-PROCESS cmd="set" data="core_video_blank_image=$${conf_dir}/freeswitch-logo.png"/>
<section name="configuration" description="Various Configuration">
<configuration name="modules.conf" description="Modules">
<modules>
<load module="mod_console"/>
<load module="mod_loopback"/>
<load module="mod_commands"/>
<load module="mod_sndfile"/>
<load module="mod_dptools"/>
<load module="mod_tone_stream"/>
<load module="mod_test"/>
<load module="mod_opus"/>
</modules>
</configuration>
<configuration name="console.conf" description="Console Logger">
<mappings>
<map name="all" value="console,debug,info,notice,warning,err,crit,alert"/>
</mappings>
<settings>
<param name="colorize" value="true"/>
<param name="loglevel" value="debug"/>
</settings>
</configuration>
<configuration name="switch.conf" description="Core Configuration">
<default-ptimes>
</default-ptimes>
<settings>
<param name="colorize-console" value="false"/>
<param name="dialplan-timestamps" value="false"/>
<param name="loglevel" value="debug"/>
<param name="rtp-start-port" value="1234"/>
<param name="rtp-end-port" value="1234"/>
</settings>
</configuration>
<configuration name="timezones.conf" description="Timezones">
<timezones>
<zone name="GMT" value="GMT0" />
</timezones>
</configuration>
</section>
<section name="dialplan" description="Regex/XML Dialplan">
<context name="test">
<extension>
<condition field="${sip_h_X-COUNTDOWN}" expression="^0$" break="on-true">
<action application="answer"/>
<action application="playback" data="tone_stream://%(251,0,1004);loops=-1"/>
</condition>
<condition field="${sip_h_X-COUNTDOWN}" expression="^(\d+)$" break="never">
<action application="export" data="_nolocal_sip_h_X-COUNTDOWN=${expr($1 - 1)}"/>
<anti-action application="export" data="_nolocal_sip_h_X-COUNTDOWN=10"/>
</condition>
<condition>
<action application="bridge" data="sofia/test/1234@127.0.0.1"/>
</condition>
</extension>
</context>
</section>
</document>
+47
View File
@@ -0,0 +1,47 @@
<?xml version="1.0"?>
<document type="freeswitch/xml">
<X-PRE-PROCESS cmd="set" data="local_ip_v4=127.0.0.1"/>
<X-PRE-PROCESS cmd="set" data="domain=127.0.0.1"/>
<section name="configuration" description="Configuration">
<configuration name="modules.conf" description="Modules">
<modules>
<load module="mod_console"/>
<load module="mod_loopback"/>
<load module="mod_dptools"/>
<load module="mod_dialplan_xml"/>
<load module="mod_sndfile"/>
<load module="mod_verto"/>
</modules>
</configuration>
<configuration name="switch.conf" description="Core Configuration">
<settings>
<param name="colorize-console" value="false"/>
<param name="loglevel" value="debug"/>
<param name="rtp-start-port" value="16384"/>
<param name="rtp-end-port" value="16484"/>
</settings>
</configuration>
<configuration name="console.conf" description="Console Logger">
<mappings>
<map name="all" value="console,debug,info,notice,warning,err,crit,alert"/>
</mappings>
<settings>
<param name="colorize" value="false"/>
<param name="loglevel" value="debug"/>
</settings>
</configuration>
<configuration name="timezones.conf" description="Timezones">
<timezones>
<zone name="GMT" value="GMT0"/>
</timezones>
</configuration>
<X-PRE-PROCESS cmd="include" data="verto.conf.xml"/>
</section>
</document>
+36
View File
@@ -0,0 +1,36 @@
<configuration name="verto.conf" description="HTML5 Verto Endpoint (test)">
<settings>
<param name="debug" value="0"/>
</settings>
<profiles>
<profile name="test-v4">
<!--
bind-local without "secure" → plain TCP. Port 33081 chosen to avoid
clashing with a default vanilla install on 8081.
-->
<param name="bind-local" value="127.0.0.1:33081"/>
<param name="force-register-domain" value="127.0.0.1"/>
<param name="userauth" value="true"/>
<param name="blind-reg" value="false"/>
<param name="rtp-ip" value="127.0.0.1"/>
<param name="timer-name" value="soft"/>
<!--
vhosts block is REQUIRED for http_run() to be invoked
(see mod_verto.c:2041,2061 — KWS_HTTP flag depends on it).
No auth-realm here, so requests bypass the 401 challenge and
reach the body-read path (which is itself pre-auth anyway).
-->
<vhosts>
<vhost domain="127.0.0.1">
<param name="alias" value="localhost"/>
<param name="root" value="."/>
<param name="index" value="index.html"/>
</vhost>
</vhosts>
</profile>
</profiles>
</configuration>
+172
View File
@@ -0,0 +1,172 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2026, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Dmitry Verenitsin <morbit85@gmail.com>
*
* switch_stun.c -- tests STUN (https://www.rfc-editor.org/rfc/rfc5389).
*/
#include <switch.h>
#include <switch_stun.h>
#include <test/switch_test.h>
FST_CORE_BEGIN("./conf_stun")
{
FST_SUITE_BEGIN(switch_stun)
{
FST_SETUP_BEGIN()
{
}
FST_SETUP_END()
FST_TEARDOWN_BEGIN()
{
}
FST_TEARDOWN_END()
FST_TEST_BEGIN(test_stun_add_binded_address_ipv6)
{
/*
* Encode an IPv6 XOR-MAPPED-ADDRESS attribute and verify the
* attribute type, length, address family, and the raw 16-byte
* address payload at its expected offset inside the value.
*/
uint8_t buf[512];
switch_stun_packet_t *packet;
switch_stun_packet_attribute_t *attr;
const char *ipv6_str = "2001:db8::dead:beef";
uint8_t expected[16];
uint8_t *value_bytes;
memset(buf, 0, sizeof(buf));
packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_RESPONSE, NULL, buf);
fst_xcheck(inet_pton(AF_INET6, ipv6_str, expected) == 1, "test IPv6 literal parses");
switch_stun_packet_attribute_add_binded_address(packet, (char *)ipv6_str, 12345, AF_INET6);
attr = (switch_stun_packet_attribute_t *)packet->first_attribute;
fst_xcheck(ntohs(attr->type) == SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS, "attribute type is XOR_MAPPED_ADDRESS");
fst_xcheck(ntohs(attr->length) == 20, "attribute length is 20 for IPv6");
/* Attribute value layout: wasted(1) + family(1) + port(2) + address(16). */
value_bytes = (uint8_t *)attr->value;
fst_xcheck(value_bytes[1] == 2, "attribute family byte is 2 for IPv6");
fst_xcheck(memcmp(value_bytes + 4, expected, 16) == 0, "16-byte IPv6 address written at offset 4 of attribute value");
}
FST_TEST_END()
FST_TEST_BEGIN(test_stun_add_xor_binded_address_ipv6)
{
/*
* Encode then decode an IPv6 XOR-MAPPED-ADDRESS attribute and
* confirm the round-trip recovers the original IPv6 string
* the write path must XOR the address with the transaction ID
* symmetrically to the read path.
*/
uint8_t buf[512];
switch_stun_packet_t *packet;
switch_stun_packet_attribute_t *attr;
const char *ipv6_str = "2001:db8::dead:beef";
char out_ip[64] = { 0 };
uint16_t out_port = 0;
memset(buf, 0, sizeof(buf));
packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_RESPONSE, NULL, buf);
switch_stun_packet_attribute_add_xor_binded_address(packet, (char *)ipv6_str, 12345, AF_INET6);
attr = (switch_stun_packet_attribute_t *)packet->first_attribute;
fst_xcheck(ntohs(attr->type) == SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS, "attribute type is XOR_MAPPED_ADDRESS");
fst_xcheck(ntohs(attr->length) == 20, "attribute length is 20 for IPv6");
switch_stun_packet_attribute_get_xor_mapped_address(attr, &packet->header, out_ip, sizeof(out_ip), &out_port);
fst_check_string_equals(out_ip, ipv6_str);
}
FST_TEST_END()
FST_TEST_BEGIN(test_stun_add_binded_address_ipv4)
{
/*
* Encode an IPv4 XOR-MAPPED-ADDRESS attribute and verify the
* attribute type, length, address family, and the raw 4-byte
* address payload at its expected offset inside the value.
*/
uint8_t buf[512];
switch_stun_packet_t *packet;
switch_stun_packet_attribute_t *attr;
const char *ipv4_str = "192.0.2.42";
uint8_t expected[4];
uint8_t *value_bytes;
memset(buf, 0, sizeof(buf));
packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_RESPONSE, NULL, buf);
fst_xcheck(inet_pton(AF_INET, ipv4_str, expected) == 1, "test IPv4 literal parses");
switch_stun_packet_attribute_add_binded_address(packet, (char *)ipv4_str, 12345, AF_INET);
attr = (switch_stun_packet_attribute_t *)packet->first_attribute;
fst_xcheck(ntohs(attr->type) == SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS, "attribute type is XOR_MAPPED_ADDRESS");
fst_xcheck(ntohs(attr->length) == 8, "attribute length is 8 for IPv4");
/* Attribute value layout: wasted(1) + family(1) + port(2) + address(4). */
value_bytes = (uint8_t *)attr->value;
fst_xcheck(value_bytes[1] == 1, "attribute family byte is 1 for IPv4");
fst_xcheck(memcmp(value_bytes + 4, expected, 4) == 0, "4-byte IPv4 address written at offset 4 of attribute value");
}
FST_TEST_END()
FST_TEST_BEGIN(test_stun_add_xor_binded_address_ipv4)
{
/*
* Encode then decode an IPv4 XOR-MAPPED-ADDRESS attribute and
* confirm the round-trip recovers the original IPv4 string
* the write path must XOR the address with the magic cookie
* symmetrically to the read path.
*/
uint8_t buf[512];
switch_stun_packet_t *packet;
switch_stun_packet_attribute_t *attr;
const char *ipv4_str = "192.0.2.42";
char out_ip[64] = { 0 };
uint16_t out_port = 0;
memset(buf, 0, sizeof(buf));
packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_RESPONSE, NULL, buf);
switch_stun_packet_attribute_add_xor_binded_address(packet, (char *)ipv4_str, 12345, AF_INET);
attr = (switch_stun_packet_attribute_t *)packet->first_attribute;
fst_xcheck(ntohs(attr->type) == SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS, "attribute type is XOR_MAPPED_ADDRESS");
fst_xcheck(ntohs(attr->length) == 8, "attribute length is 8 for IPv4");
switch_stun_packet_attribute_get_xor_mapped_address(attr, &packet->header, out_ip, sizeof(out_ip), &out_port);
fst_check_string_equals(out_ip, ipv4_str);
}
FST_TEST_END()
}
FST_SUITE_END()
}
FST_CORE_END()
+63
View File
@@ -124,6 +124,69 @@ FST_TEST_BEGIN(b64_pad1)
}
FST_TEST_END()
#define test_uri_count 6
/* Currently tests only clear_uri() */
FST_TEST_BEGIN(test_switch_http_parse_header)
{
int i = 0;
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_http_request_t request = {0};
char bad_uris[][200] = {
"/t/o/o/_/l/o/n/g/_/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/2/3/4",
"without_a_slash/",
};
char raw_uris[test_uri_count][200] = {
"/////////uri1",
"/././././uri2",
"/uri3/uri3_1/.//uri3_2/../../uri3_3",
"/../../../uri4",
"/uri5/uri5_1/",
"/uri6/uri6_1",
};
const char clear_uris[test_uri_count][200] = {
"/uri1",
"/uri2",
"/uri3/uri3_3",
"/uri4",
"/uri5/uri5_1",
"/uri6/uri6_1",
};
for (i = 0; i < (sizeof(bad_uris) / sizeof(bad_uris[0])); i++) {
char bad_header[256];
const char *bad_uri = bad_uris[i];
/* Use precision specifier to suppress false-positive "format-truncation" warning. */
snprintf(bad_header, sizeof(bad_header), "GET %.199s HTTP/1.1\r\n\r\nBODY", bad_uri);
fst_check((status = switch_http_parse_header(bad_header, sizeof(bad_header), &request)) == SWITCH_STATUS_FALSE);
if (status == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Bad uri parsed [%d]: [%s]\n", i, request.uri);
switch_http_free_request(&request);
}
}
for (i = 0; i < test_uri_count; i++) {
char raw_header[256];
const char *clear_uri = clear_uris[i];
const char *raw_uri = raw_uris[i];
/* Use precision specifier to suppress false-positive "format-truncation" warning. */
snprintf(raw_header, sizeof(raw_header), "GET %.199s HTTP/1.1\r\n\r\nBODY", raw_uri);
fst_check((status = switch_http_parse_header(raw_header, sizeof(raw_header), &request)) == SWITCH_STATUS_SUCCESS);
fst_check_string_equals(clear_uri, request.uri);
if (status == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "URI [%d]: [%s] => [%s]\n", i, raw_uri, request.uri);
switch_http_free_request(&request);
}
}
}
FST_TEST_END()
FST_SUITE_END()
FST_MINCORE_END()
+316
View File
@@ -0,0 +1,316 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2026, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Dmitry Verenitsin <dmitry.verenitsin@signalwire.com>
*
*
* test_mod_verto.c -- Tests for mod_verto
*
*/
#include <switch.h>
#include <test/switch_test.h>
#define VERTO_TEST_HOST "127.0.0.1"
#define VERTO_TEST_PORT 33081
/* Must match HTTP_POST_MAX_BODY in src/mod/endpoints/mod_verto/mod_verto.c */
#define VERTO_POST_MAX_BODY (10 * 1024 * 1024)
static switch_status_t verto_connect(switch_socket_t **sock_out, switch_memory_pool_t *pool)
{
switch_sockaddr_t *addr = NULL;
switch_socket_t *sock = NULL;
int attempts;
if (switch_sockaddr_info_get(&addr, VERTO_TEST_HOST, SWITCH_UNSPEC,
VERTO_TEST_PORT, 0, pool) != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_FALSE;
}
for (attempts = 0; attempts < 50; attempts++) {
if (switch_socket_create(&sock, switch_sockaddr_get_family(addr),
SOCK_STREAM, SWITCH_PROTO_TCP, pool) != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_FALSE;
}
switch_socket_opt_set(sock, SWITCH_SO_TCP_NODELAY, 1);
if (switch_socket_connect(sock, addr) == SWITCH_STATUS_SUCCESS) {
*sock_out = sock;
return SWITCH_STATUS_SUCCESS;
}
switch_socket_close(sock);
sock = NULL;
switch_yield(100000);
}
return SWITCH_STATUS_FALSE;
}
static switch_status_t send_all(switch_socket_t *sock, const char *buf, switch_size_t len)
{
switch_size_t remaining = len;
const char *p = buf;
while (remaining > 0) {
switch_size_t n = remaining;
if (switch_socket_send(sock, p, &n) != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_FALSE;
}
if (n == 0) {
return SWITCH_STATUS_FALSE;
}
p += n;
remaining -= n;
}
return SWITCH_STATUS_SUCCESS;
}
static switch_size_t read_status_line(switch_socket_t *sock, char *out, switch_size_t cap)
{
switch_size_t got = 0;
while (got < cap - 1) {
switch_size_t want = cap - 1 - got;
if (switch_socket_recv(sock, out + got, &want) != SWITCH_STATUS_SUCCESS || want == 0) {
break;
}
got += want;
if (memchr(out, '\n', got)) break;
}
out[got] = '\0';
return got;
}
FST_CORE_DB_BEGIN("./conf_verto")
{
FST_SUITE_BEGIN(test_mod_verto)
{
FST_SETUP_BEGIN()
{
fst_requires_module("mod_verto");
switch_yield(500000);
}
FST_SETUP_END()
FST_TEARDOWN_BEGIN()
{
}
FST_TEARDOWN_END()
FST_TEST_BEGIN(post_at_cap_returns_413)
{
switch_memory_pool_t *pool = NULL;
switch_socket_t *sock = NULL;
char req[256];
char resp[64] = { 0 };
switch_size_t req_len;
do {
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
fst_fail("could not allocate memory pool");
break;
}
if (verto_connect(&sock, pool) != SWITCH_STATUS_SUCCESS) {
fst_fail("could not connect to verto listener");
break;
}
req_len = switch_snprintf(req, sizeof(req),
"POST / HTTP/1.1\r\n"
"Host: " VERTO_TEST_HOST "\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: %d\r\n"
"\r\n",
VERTO_POST_MAX_BODY);
if (send_all(sock, req, req_len) != SWITCH_STATUS_SUCCESS) {
fst_fail("could not send request");
break;
}
read_status_line(sock, resp, sizeof(resp));
fst_check_string_starts_with(resp, "HTTP/1.1 413");
} while (0);
if (sock) switch_socket_close(sock);
if (pool) switch_core_destroy_memory_pool(&pool);
}
FST_TEST_END()
FST_TEST_BEGIN(post_small_body_parsed)
{
switch_memory_pool_t *pool = NULL;
switch_socket_t *sock = NULL;
const switch_size_t body_len = 32 * 1024;
char *body = NULL;
char req[256];
char resp[64] = { 0 };
switch_size_t req_len;
do {
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
fst_fail("could not allocate memory pool");
break;
}
if (verto_connect(&sock, pool) != SWITCH_STATUS_SUCCESS) {
fst_fail("could not connect to verto listener");
break;
}
body = malloc(body_len);
if (!body) {
fst_fail("could not allocate body buffer");
break;
}
memset(body, 'x', body_len);
req_len = switch_snprintf(req, sizeof(req),
"POST / HTTP/1.1\r\n"
"Host: " VERTO_TEST_HOST "\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: %" SWITCH_SIZE_T_FMT "\r\n"
"\r\n",
body_len);
if (send_all(sock, req, req_len) != SWITCH_STATUS_SUCCESS) {
fst_fail("could not send headers");
break;
}
if (send_all(sock, body, body_len) != SWITCH_STATUS_SUCCESS) {
fst_fail("could not send body");
break;
}
read_status_line(sock, resp, sizeof(resp));
fst_check_string_starts_with(resp, "HTTP/1.1 ");
fst_xcheck(strncmp(resp, "HTTP/1.1 413", 12) != 0,
"server returned 413 below cap");
} while (0);
free(body);
if (sock) switch_socket_close(sock);
if (pool) switch_core_destroy_memory_pool(&pool);
}
FST_TEST_END()
FST_TEST_BEGIN(post_large_body_no_overflow)
{
switch_memory_pool_t *pool = NULL;
switch_socket_t *sock = NULL;
const switch_size_t body_len = 8 * 1024 * 1024;
char *body = NULL;
char req[256];
char resp[64] = { 0 };
switch_size_t req_len;
do {
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
fst_fail("could not allocate memory pool");
break;
}
if (verto_connect(&sock, pool) != SWITCH_STATUS_SUCCESS) {
fst_fail("could not connect to verto listener");
break;
}
body = malloc(body_len);
if (!body) {
fst_fail("could not allocate body buffer");
break;
}
memset(body, 'x', body_len);
req_len = switch_snprintf(req, sizeof(req),
"POST / HTTP/1.1\r\n"
"Host: " VERTO_TEST_HOST "\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: %" SWITCH_SIZE_T_FMT "\r\n"
"\r\n",
body_len);
if (send_all(sock, req, req_len) != SWITCH_STATUS_SUCCESS) {
fst_fail("could not send headers");
break;
}
if (send_all(sock, body, body_len) != SWITCH_STATUS_SUCCESS) {
fst_fail("could not send body");
break;
}
read_status_line(sock, resp, sizeof(resp));
fst_check_string_starts_with(resp, "HTTP/1.1 ");
fst_xcheck(strncmp(resp, "HTTP/1.1 413", 12) != 0,
"server returned 413 below cap");
} while (0);
free(body);
if (sock) switch_socket_close(sock);
if (pool) switch_core_destroy_memory_pool(&pool);
}
FST_TEST_END()
FST_TEST_BEGIN(post_overflow_length_returns_413)
{
switch_memory_pool_t *pool = NULL;
switch_socket_t *sock = NULL;
char req[256];
char resp[64] = { 0 };
switch_size_t req_len;
do {
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
fst_fail("could not allocate memory pool");
break;
}
if (verto_connect(&sock, pool) != SWITCH_STATUS_SUCCESS) {
fst_fail("could not connect to verto listener");
break;
}
req_len = switch_snprintf(req, sizeof(req),
"POST / HTTP/1.1\r\n"
"Host: " VERTO_TEST_HOST "\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: 9999999999\r\n"
"\r\n");
if (send_all(sock, req, req_len) != SWITCH_STATUS_SUCCESS) {
fst_fail("could not send request");
break;
}
read_status_line(sock, resp, sizeof(resp));
fst_check_string_starts_with(resp, "HTTP/1.1 413");
} while (0);
if (sock) switch_socket_close(sock);
if (pool) switch_core_destroy_memory_pool(&pool);
}
FST_TEST_END()
}
FST_SUITE_END()
}
FST_CORE_END()
+1 -1
View File
@@ -29,7 +29,7 @@
<Target Name="SofiaSipDownloadTarget" BeforeTargets="CustomBuild;PreBuildEvent;" DependsOnTargets="7za">
<DownloadPackageTask
package="https://github.com/freeswitch/sofia-sip/archive/master.zip"
package="https://github.com/freeswitch/sofia-sip/archive/$(SofiaSipVersion).zip"
expectfileordirectory="$(BaseDir)libs\sofia-sip\configure.ac"
outputfolder=""
outputfilename="sofia-sip-$(SofiaSipVersion).zip"
+1 -1
View File
@@ -4,7 +4,7 @@
<Import Project="basedir.props" Condition=" '$(BaseDirImported)' == ''"/>
</ImportGroup>
<PropertyGroup Label="UserMacros">
<libksVersion>2.0.7</libksVersion>
<libksVersion>2.0.11</libksVersion>
<libksBuildNumber>0</libksBuildNumber>
</PropertyGroup>
<PropertyGroup>