mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-07-04 19:31:56 +00:00
Merge branch 'master' into v1.11
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 @@
|
||||
1.11.0-release
|
||||
1.11.1-release
|
||||
|
||||
@@ -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
@@ -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,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
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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="v=analytics1;url=https://example.com/redress";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="v=analytics1;url=https://example.com/redress";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="v=analytics1;url=https://example.com/redress";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="Call Rejected""/>
|
||||
<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="v=analytics1;url=https://example.com/redress";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="v=analytics1;url=https://example.com/redress";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()
|
||||
@@ -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);
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -227,6 +227,7 @@ static char *EVENT_NAMES[] = {
|
||||
"DEVICE_STATE",
|
||||
"TEXT",
|
||||
"SHUTDOWN_REQUESTED",
|
||||
"CERT_RELOAD",
|
||||
"ALL"
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user