summaryrefslogtreecommitdiff
path: root/res
diff options
context:
space:
mode:
Diffstat (limited to 'res')
-rw-r--r--res/res_ari_applications.c4
-rw-r--r--res/res_ari_asterisk.c4
-rw-r--r--res/res_ari_bridges.c4
-rw-r--r--res/res_ari_channels.c4
-rw-r--r--res/res_ari_device_states.c4
-rw-r--r--res/res_ari_endpoints.c4
-rw-r--r--res/res_ari_events.c33
-rw-r--r--res/res_ari_mailboxes.c4
-rw-r--r--res/res_ari_playbacks.c4
-rw-r--r--res/res_ari_recordings.c4
-rw-r--r--res/res_ari_sounds.c4
-rw-r--r--res/res_calendar.c2
-rw-r--r--res/res_calendar_icalendar.c2
-rw-r--r--res/res_config_pgsql.c2
-rw-r--r--res/res_corosync.c29
-rw-r--r--res/res_http_post.c19
-rw-r--r--res/res_monitor.c54
-rw-r--r--res/res_musiconhold.c129
-rw-r--r--res/res_pjsip.c102
-rw-r--r--res/res_pjsip/config_transport.c46
-rw-r--r--res/res_pjsip/include/res_pjsip_private.h23
-rw-r--r--res/res_pjsip/location.c62
-rw-r--r--res/res_pjsip/pjsip_configuration.c71
-rw-r--r--res/res_pjsip/pjsip_distributor.c242
-rw-r--r--res/res_pjsip/pjsip_message_ip_updater.c56
-rw-r--r--res/res_pjsip/pjsip_transport_events.c366
-rw-r--r--res/res_pjsip/presence_xml.c25
-rw-r--r--res/res_pjsip_dialog_info_body_generator.c10
-rw-r--r--res/res_pjsip_messaging.c2
-rw-r--r--res/res_pjsip_mwi.c87
-rw-r--r--res/res_pjsip_nat.c12
-rw-r--r--res/res_pjsip_outbound_registration.c136
-rw-r--r--res/res_pjsip_pidf_body_generator.c4
-rw-r--r--res/res_pjsip_pidf_eyebeam_body_supplement.c34
-rw-r--r--res/res_pjsip_pubsub.c8
-rw-r--r--res/res_pjsip_refer.c8
-rw-r--r--res/res_pjsip_registrar.c109
-rw-r--r--res/res_pjsip_sdp_rtp.c50
-rw-r--r--res/res_pjsip_session.c81
-rw-r--r--res/res_pjsip_session.exports.in2
-rw-r--r--res/res_pjsip_t38.c42
-rw-r--r--res/res_pjsip_transport_management.c58
-rw-r--r--res/res_pjsip_transport_websocket.c21
-rw-r--r--res/res_pjsip_xpidf_body_generator.c2
-rw-r--r--res/res_rtp_asterisk.c228
-rw-r--r--res/res_smdi.c10
-rw-r--r--res/res_stasis.c20
-rw-r--r--res/res_stasis_device_state.c4
-rw-r--r--res/res_stasis_snoop.c22
-rw-r--r--res/res_xmpp.c99
-rw-r--r--res/srtp/srtp_compat.h4
-rw-r--r--res/stasis/control.c118
52 files changed, 1975 insertions, 499 deletions
diff --git a/res/res_ari_applications.c b/res/res_ari_applications.c
index cb12e84c8..290719d36 100644
--- a/res/res_ari_applications.c
+++ b/res/res_ari_applications.c
@@ -502,6 +502,10 @@ static int unload_module(void)
static int load_module(void)
{
int res = 0;
+
+ CHECK_ARI_MODULE_LOADED();
+
+
stasis_app_ref();
res |= ast_ari_add_handler(&applications);
if (res) {
diff --git a/res/res_ari_asterisk.c b/res/res_ari_asterisk.c
index 1a574aaaf..73e4d0ce3 100644
--- a/res/res_ari_asterisk.c
+++ b/res/res_ari_asterisk.c
@@ -1223,6 +1223,10 @@ static int unload_module(void)
static int load_module(void)
{
int res = 0;
+
+ CHECK_ARI_MODULE_LOADED();
+
+
stasis_app_ref();
res |= ast_ari_add_handler(&asterisk);
if (res) {
diff --git a/res/res_ari_bridges.c b/res/res_ari_bridges.c
index 69d4d6ed5..b92333095 100644
--- a/res/res_ari_bridges.c
+++ b/res/res_ari_bridges.c
@@ -1415,6 +1415,10 @@ static int unload_module(void)
static int load_module(void)
{
int res = 0;
+
+ CHECK_ARI_MODULE_LOADED();
+
+
stasis_app_ref();
res |= ast_ari_add_handler(&bridges);
if (res) {
diff --git a/res/res_ari_channels.c b/res/res_ari_channels.c
index f59f20634..621767980 100644
--- a/res/res_ari_channels.c
+++ b/res/res_ari_channels.c
@@ -2479,6 +2479,10 @@ static int unload_module(void)
static int load_module(void)
{
int res = 0;
+
+ CHECK_ARI_MODULE_LOADED();
+
+
stasis_app_ref();
res |= ast_ari_add_handler(&channels);
if (res) {
diff --git a/res/res_ari_device_states.c b/res/res_ari_device_states.c
index a3711e6eb..fe1817d5d 100644
--- a/res/res_ari_device_states.c
+++ b/res/res_ari_device_states.c
@@ -333,6 +333,10 @@ static int unload_module(void)
static int load_module(void)
{
int res = 0;
+
+ CHECK_ARI_MODULE_LOADED();
+
+
stasis_app_ref();
res |= ast_ari_add_handler(&deviceStates);
if (res) {
diff --git a/res/res_ari_endpoints.c b/res/res_ari_endpoints.c
index 43d255898..a46b0dc61 100644
--- a/res/res_ari_endpoints.c
+++ b/res/res_ari_endpoints.c
@@ -457,6 +457,10 @@ static int unload_module(void)
static int load_module(void)
{
int res = 0;
+
+ CHECK_ARI_MODULE_LOADED();
+
+
stasis_app_ref();
res |= ast_ari_add_handler(&endpoints);
if (res) {
diff --git a/res/res_ari_events.c b/res/res_ari_events.c
index fd208c57b..b6a44d9b9 100644
--- a/res/res_ari_events.c
+++ b/res/res_ari_events.c
@@ -430,22 +430,29 @@ static int unload_module(void)
static int load_module(void)
{
int res = 0;
- struct ast_websocket_protocol *protocol;
- events.ws_server = ast_websocket_server_create();
- if (!events.ws_server) {
- return AST_MODULE_LOAD_DECLINE;
- }
+ CHECK_ARI_MODULE_LOADED();
- protocol = ast_websocket_sub_protocol_alloc("ari");
- if (!protocol) {
- ao2_ref(events.ws_server, -1);
- events.ws_server = NULL;
- return AST_MODULE_LOAD_DECLINE;
+ /* This is scoped to not conflict with CHECK_ARI_MODULE_LOADED */
+ {
+ struct ast_websocket_protocol *protocol;
+
+ events.ws_server = ast_websocket_server_create();
+ if (!events.ws_server) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ protocol = ast_websocket_sub_protocol_alloc("ari");
+ if (!protocol) {
+ ao2_ref(events.ws_server, -1);
+ events.ws_server = NULL;
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ protocol->session_attempted = ast_ari_events_event_websocket_ws_attempted_cb;
+ protocol->session_established = ast_ari_events_event_websocket_ws_established_cb;
+ res |= ast_websocket_server_add_protocol2(events.ws_server, protocol);
}
- protocol->session_attempted = ast_ari_events_event_websocket_ws_attempted_cb;
- protocol->session_established = ast_ari_events_event_websocket_ws_established_cb;
- res |= ast_websocket_server_add_protocol2(events.ws_server, protocol);
+
stasis_app_ref();
res |= ast_ari_add_handler(&events);
if (res) {
diff --git a/res/res_ari_mailboxes.c b/res/res_ari_mailboxes.c
index f85541cf0..600ecfd48 100644
--- a/res/res_ari_mailboxes.c
+++ b/res/res_ari_mailboxes.c
@@ -339,6 +339,10 @@ static int unload_module(void)
static int load_module(void)
{
int res = 0;
+
+ CHECK_ARI_MODULE_LOADED();
+
+
stasis_app_ref();
res |= ast_ari_add_handler(&mailboxes);
if (res) {
diff --git a/res/res_ari_playbacks.c b/res/res_ari_playbacks.c
index 25e211c55..106463b5b 100644
--- a/res/res_ari_playbacks.c
+++ b/res/res_ari_playbacks.c
@@ -291,6 +291,10 @@ static int unload_module(void)
static int load_module(void)
{
int res = 0;
+
+ CHECK_ARI_MODULE_LOADED();
+
+
stasis_app_ref();
res |= ast_ari_add_handler(&playbacks);
if (res) {
diff --git a/res/res_ari_recordings.c b/res/res_ari_recordings.c
index 29720a84a..c43148d83 100644
--- a/res/res_ari_recordings.c
+++ b/res/res_ari_recordings.c
@@ -807,6 +807,10 @@ static int unload_module(void)
static int load_module(void)
{
int res = 0;
+
+ CHECK_ARI_MODULE_LOADED();
+
+
stasis_app_ref();
res |= ast_ari_add_handler(&recordings);
if (res) {
diff --git a/res/res_ari_sounds.c b/res/res_ari_sounds.c
index 6d09d2cbe..e58ecd1cf 100644
--- a/res/res_ari_sounds.c
+++ b/res/res_ari_sounds.c
@@ -221,6 +221,10 @@ static int unload_module(void)
static int load_module(void)
{
int res = 0;
+
+ CHECK_ARI_MODULE_LOADED();
+
+
stasis_app_ref();
res |= ast_ari_add_handler(&sounds);
if (res) {
diff --git a/res/res_calendar.c b/res/res_calendar.c
index 3725c9435..298970a92 100644
--- a/res/res_calendar.c
+++ b/res/res_calendar.c
@@ -735,7 +735,7 @@ static void *do_notify(void *data)
struct ast_channel *chan = NULL;
struct ast_variable *itervar;
char *tech, *dest;
- char buf[8];
+ char buf[33];
struct ast_format_cap *caps;
tech = ast_strdupa(event->owner->notify_channel);
diff --git a/res/res_calendar_icalendar.c b/res/res_calendar_icalendar.c
index 8ac905174..a6ce62708 100644
--- a/res/res_calendar_icalendar.c
+++ b/res/res_calendar_icalendar.c
@@ -335,7 +335,7 @@ static void icalendar_add_event(icalcomponent *comp, struct icaltime_span *span,
start_time = icaltime_current_time_with_zone(icaltimezone_get_utc_timezone());
end_time = icaltime_current_time_with_zone(icaltimezone_get_utc_timezone());
end_time.second += pvt->owner->timeframe * 60;
- icaltime_normalize(end_time);
+ end_time = icaltime_normalize(end_time);
for (iter = icalcomponent_get_first_component(pvt->data, ICAL_VEVENT_COMPONENT);
iter;
diff --git a/res/res_config_pgsql.c b/res/res_config_pgsql.c
index e74b73036..b0a24c464 100644
--- a/res/res_config_pgsql.c
+++ b/res/res_config_pgsql.c
@@ -1329,7 +1329,7 @@ static int require_pgsql(const char *database, const char *tablename, va_list ap
/* Size is minimum length; make it at least 50% greater,
* just to be sure, because PostgreSQL doesn't support
* resizing columns. */
- snprintf(fieldtype, sizeof(fieldtype), "CHAR(%d)",
+ snprintf(fieldtype, sizeof(fieldtype), "CHAR(%hhu)",
size < 15 ? size * 2 :
(size * 3 / 2 > 255) ? 255 : size * 3 / 2);
} else if (type == RQ_INTEGER1 || type == RQ_UINTEGER1 || type == RQ_INTEGER2) {
diff --git a/res/res_corosync.c b/res/res_corosync.c
index 6bbbc34b9..ce94e4151 100644
--- a/res/res_corosync.c
+++ b/res/res_corosync.c
@@ -79,6 +79,15 @@ struct corosync_node {
struct ast_sockaddr addr;
};
+/*! \brief Corosync ipc dispatch/request and reply size */
+#define COROSYNC_IPC_BUFFER_SIZE (8192 * 128)
+
+/*! \brief Version of pthread_create to ensure stack is large enough */
+#define corosync_pthread_create_background(a, b, c, d) \
+ ast_pthread_create_stack(a, b, c, d, \
+ (AST_BACKGROUND_STACKSIZE + (3 * COROSYNC_IPC_BUFFER_SIZE)), \
+ __FILE__, __FUNCTION__, __LINE__, #c)
+
static struct corosync_node *corosync_node_alloc(struct ast_event *event)
{
struct corosync_node *node;
@@ -810,10 +819,21 @@ static char *corosync_show_members(struct ast_cli_entry *e, int cmd, struct ast_
for (i = 1, cs_err = cpg_iteration_next(cpg_iter, &cpg_desc);
cs_err == CS_OK;
cs_err = cpg_iteration_next(cpg_iter, &cpg_desc), i++) {
+#ifdef HAVE_COROSYNC_CFG_STATE_TRACK
corosync_cfg_node_address_t addrs[8];
int num_addrs = 0;
unsigned int j;
+#endif
+
+ ast_cli(a->fd, "=== Node %u\n", i);
+ ast_cli(a->fd, "=== --> Group: %s\n", cpg_desc.group.value);
+#ifdef HAVE_COROSYNC_CFG_STATE_TRACK
+ /*
+ * Corosync 2.x cfg lib needs to allocate 1M on stack after calling
+ * corosync_cfg_get_node_addrs. netconsole thread has allocated only 0.5M
+ * resulting in crash.
+ */
cs_err = corosync_cfg_get_node_addrs(cfg_handle, cpg_desc.nodeid,
ARRAY_LEN(addrs), &num_addrs, addrs);
if (cs_err != CS_OK) {
@@ -821,9 +841,6 @@ static char *corosync_show_members(struct ast_cli_entry *e, int cmd, struct ast_
continue;
}
- ast_cli(a->fd, "=== Node %u\n", i);
- ast_cli(a->fd, "=== --> Group: %s\n", cpg_desc.group.value);
-
for (j = 0; j < num_addrs; j++) {
struct sockaddr *sa = (struct sockaddr *) addrs[j].address;
size_t sa_len = (size_t) addrs[j].address_length;
@@ -833,7 +850,9 @@ static char *corosync_show_members(struct ast_cli_entry *e, int cmd, struct ast_
ast_cli(a->fd, "=== --> Address %u: %s\n", j + 1, buf);
}
-
+#else
+ ast_cli(a->fd, "=== --> Nodeid: %"PRIu32"\n", cpg_desc.nodeid);
+#endif
}
ast_cli(a->fd, "===\n"
@@ -1159,7 +1178,7 @@ static int load_module(void)
goto failed;
}
- if (ast_pthread_create_background(&dispatch_thread.id, NULL,
+ if (corosync_pthread_create_background(&dispatch_thread.id, NULL,
dispatch_thread_handler, NULL)) {
ast_log(LOG_ERROR, "Error starting CPG dispatch thread.\n");
goto failed;
diff --git a/res/res_http_post.c b/res/res_http_post.c
index 2ee792af9..3e1ed03f6 100644
--- a/res/res_http_post.c
+++ b/res/res_http_post.c
@@ -57,6 +57,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#ifdef GMIME_TYPE_CONTENT_TYPE
#define AST_GMIME_VER_24
#endif
+#if GMIME_MAJOR_VERSION >= 3
+#define AST_GMIME_VER_30
+#endif
/* just a little structure to hold callback info for gmime */
struct mime_cbinfo {
@@ -86,7 +89,11 @@ static void post_raw(GMimePart *part, const char *post_dir, const char *fn)
stream = g_mime_stream_fs_new(fd);
+#ifdef AST_GMIME_VER_30
+ content = g_mime_part_get_content(part);
+#else
content = g_mime_part_get_content_object(part);
+#endif
g_mime_data_wrapper_write_to_stream(content, stream);
g_mime_stream_flush(stream);
@@ -109,7 +116,11 @@ static GMimeMessage *parse_message(FILE *f)
g_object_unref(stream);
- message = g_mime_parser_construct_message(parser);
+ message = g_mime_parser_construct_message(parser
+#ifdef AST_GMIME_VER_30
+ , NULL
+#endif
+ );
g_object_unref(parser);
@@ -488,7 +499,11 @@ static int reload(void)
static int load_module(void)
{
- g_mime_init(0);
+ g_mime_init(
+#ifndef AST_GMIME_VER_30
+ 0
+#endif
+ );
__ast_http_post_load(0);
diff --git a/res/res_monitor.c b/res/res_monitor.c
index ebf98439c..c4ee674f9 100644
--- a/res/res_monitor.c
+++ b/res/res_monitor.c
@@ -62,17 +62,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<syntax>
<parameter name="file_format" argsep=":">
<argument name="file_format" required="true">
- <para>optional, if not set, defaults to <literal>wav</literal></para>
+ <para>Optional. If not set, defaults to <literal>wav</literal></para>
</argument>
<argument name="urlbase" />
</parameter>
<parameter name="fname_base">
- <para>if set, changes the filename used to the one specified.</para>
+ <para>If set, changes the filename used to the one specified.</para>
</parameter>
<parameter name="options">
<optionlist>
<option name="m">
- <para>when the recording ends mix the two leg files into one and
+ <para>When the recording ends mix the two leg files into one and
delete the two leg files. If the variable <variable>MONITOR_EXEC</variable>
is set, the application referenced in it will be executed instead of
soxmix/sox and the raw leg files will NOT be deleted automatically.
@@ -83,6 +83,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
will be passed on as additional arguments to <variable>MONITOR_EXEC</variable>.
Both <variable>MONITOR_EXEC</variable> and the Mix flag can be set from the
administrator interface.</para>
+ <warning><para>Do not use untrusted strings such as
+ <variable>CALLERID(num)</variable> or <variable>CALLERID(name)</variable>
+ as part of <variable>MONITOR_EXEC</variable> or
+ <variable>MONITOR_EXEC_ARGS</variable>. You risk a command injection
+ attack executing arbitrary commands if the untrusted strings aren't
+ filtered to remove dangerous characters. See function
+ <variable>FILTER()</variable>.</para></warning>
</option>
<option name="b">
<para>Don't begin recording unless a call is bridged to another channel.</para>
@@ -460,7 +467,7 @@ int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_l
LOCK_IF_NEEDED(chan, need_lock);
if (ast_channel_monitor(chan)) {
- char filename[ FILENAME_MAX ];
+ RAII_VAR(struct ast_str *, tmp, ast_str_create(1024), ast_free);
if (ast_channel_monitor(chan)->read_stream) {
ast_closestream(ast_channel_monitor(chan)->read_stream);
@@ -469,31 +476,29 @@ int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_l
ast_closestream(ast_channel_monitor(chan)->write_stream);
}
- if (ast_channel_monitor(chan)->filename_changed && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
+ if (tmp && ast_channel_monitor(chan)->filename_changed && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
if (ast_fileexists(ast_channel_monitor(chan)->read_filename,NULL,NULL) > 0) {
- snprintf(filename, FILENAME_MAX, "%s-in", ast_channel_monitor(chan)->filename_base);
- if (ast_fileexists(filename, NULL, NULL) > 0) {
- ast_filedelete(filename, NULL);
+ ast_str_set(&tmp, 0, "%s-in", ast_channel_monitor(chan)->filename_base);
+ if (ast_fileexists(ast_str_buffer(tmp), NULL, NULL) > 0) {
+ ast_filedelete(ast_str_buffer(tmp), NULL);
}
- ast_filerename(ast_channel_monitor(chan)->read_filename, filename, ast_channel_monitor(chan)->format);
+ ast_filerename(ast_channel_monitor(chan)->read_filename, ast_str_buffer(tmp), ast_channel_monitor(chan)->format);
} else {
ast_log(LOG_WARNING, "File %s not found\n", ast_channel_monitor(chan)->read_filename);
}
- if (ast_fileexists(ast_channel_monitor(chan)->write_filename,NULL,NULL) > 0) {
- snprintf(filename, FILENAME_MAX, "%s-out", ast_channel_monitor(chan)->filename_base);
- if (ast_fileexists(filename, NULL, NULL) > 0) {
- ast_filedelete(filename, NULL);
+ if (tmp && ast_fileexists(ast_channel_monitor(chan)->write_filename,NULL,NULL) > 0) {
+ ast_str_set(&tmp, 0, "%s-out", ast_channel_monitor(chan)->filename_base);
+ if (ast_fileexists(ast_str_buffer(tmp), NULL, NULL) > 0) {
+ ast_filedelete(ast_str_buffer(tmp), NULL);
}
- ast_filerename(ast_channel_monitor(chan)->write_filename, filename, ast_channel_monitor(chan)->format);
+ ast_filerename(ast_channel_monitor(chan)->write_filename, ast_str_buffer(tmp), ast_channel_monitor(chan)->format);
} else {
ast_log(LOG_WARNING, "File %s not found\n", ast_channel_monitor(chan)->write_filename);
}
}
- if (ast_channel_monitor(chan)->joinfiles && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
- char tmp[1024];
- char tmp2[1024];
+ if (tmp && ast_channel_monitor(chan)->joinfiles && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
const char *format = !strcasecmp(ast_channel_monitor(chan)->format,"wav49") ? "WAV" : ast_channel_monitor(chan)->format;
char *fname_base = ast_channel_monitor(chan)->filename_base;
const char *execute, *execute_args;
@@ -514,16 +519,17 @@ int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_l
if (ast_strlen_zero(execute_args)) {
execute_args = "";
}
-
- snprintf(tmp, sizeof(tmp), "%s \"%s-in.%s\" \"%s-out.%s\" \"%s.%s\" %s &",
+
+ ast_str_set(&tmp, 0, delfiles ? "( " : "");
+ ast_str_append(&tmp, 0, "%s \"%s-in.%s\" \"%s-out.%s\" \"%s.%s\" %s &",
execute, fname_base, format, fname_base, format, fname_base, format,execute_args);
if (delfiles) {
- snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s-\"* ) &",tmp, fname_base); /* remove legs when done mixing */
- ast_copy_string(tmp, tmp2, sizeof(tmp));
+ /* remove legs when done mixing */
+ ast_str_append(&tmp, 0, "& rm -f \"%s-\"* ) &", fname_base);
}
- ast_debug(1,"monitor executing %s\n",tmp);
- if (ast_safe_system(tmp) == -1)
- ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
+ ast_debug(1,"monitor executing %s\n", ast_str_buffer(tmp));
+ if (ast_safe_system(ast_str_buffer(tmp)) == -1)
+ ast_log(LOG_WARNING, "Execute of %s failed.\n", ast_str_buffer(tmp));
}
if (!ast_strlen_zero(ast_channel_monitor(chan)->beep_id)) {
diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c
index c52c96428..d79151676 100644
--- a/res/res_musiconhold.c
+++ b/res/res_musiconhold.c
@@ -158,6 +158,11 @@ struct moh_files_state {
static struct ast_flags global_flags[1] = {{0}}; /*!< global MOH_ flags */
+enum kill_methods {
+ KILL_METHOD_PROCESS_GROUP = 0,
+ KILL_METHOD_PROCESS
+};
+
struct mohclass {
char name[MAX_MUSICCLASS];
char dir[256];
@@ -178,6 +183,10 @@ struct mohclass {
int pid;
time_t start;
pthread_t thread;
+ /*! Millisecond delay between kill attempts */
+ size_t kill_delay;
+ /*! Kill method */
+ enum kill_methods kill_method;
/*! Source of audio */
int srcfd;
/*! Generic timer */
@@ -678,6 +687,51 @@ static int spawn_mp3(struct mohclass *class)
return fds[0];
}
+static int killer(pid_t pid, int signum, enum kill_methods kill_method)
+{
+ switch (kill_method) {
+ case KILL_METHOD_PROCESS_GROUP:
+ return killpg(pid, signum);
+ case KILL_METHOD_PROCESS:
+ return kill(pid, signum);
+ }
+
+ return -1;
+}
+
+static void killpid(int pid, size_t delay, enum kill_methods kill_method)
+{
+ if (killer(pid, SIGHUP, kill_method) < 0) {
+ if (errno == ESRCH) {
+ return;
+ }
+ ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process '%d'?!!: %s\n", pid, strerror(errno));
+ } else {
+ ast_debug(1, "Sent HUP to pid %d%s\n", pid,
+ kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
+ }
+ usleep(delay);
+ if (killer(pid, SIGTERM, kill_method) < 0) {
+ if (errno == ESRCH) {
+ return;
+ }
+ ast_log(LOG_WARNING, "Unable to terminate MOH process '%d'?!!: %s\n", pid, strerror(errno));
+ } else {
+ ast_debug(1, "Sent TERM to pid %d%s\n", pid,
+ kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
+ }
+ usleep(delay);
+ if (killer(pid, SIGKILL, kill_method) < 0) {
+ if (errno == ESRCH) {
+ return;
+ }
+ ast_log(LOG_WARNING, "Unable to kill MOH process '%d'?!!: %s\n", pid, strerror(errno));
+ } else {
+ ast_debug(1, "Sent KILL to pid %d%s\n", pid,
+ kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
+ }
+}
+
static void *monmp3thread(void *data)
{
#define MOH_MS_INTERVAL 100
@@ -753,28 +807,7 @@ static void *monmp3thread(void *data)
class->srcfd = -1;
pthread_testcancel();
if (class->pid > 1) {
- do {
- if (killpg(class->pid, SIGHUP) < 0) {
- if (errno == ESRCH) {
- break;
- }
- ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
- }
- usleep(100000);
- if (killpg(class->pid, SIGTERM) < 0) {
- if (errno == ESRCH) {
- break;
- }
- ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
- }
- usleep(100000);
- if (killpg(class->pid, SIGKILL) < 0) {
- if (errno == ESRCH) {
- break;
- }
- ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
- }
- } while (0);
+ killpid(class->pid, class->kill_delay, class->kill_method);
class->pid = 0;
}
} else {
@@ -1328,6 +1361,7 @@ static struct mohclass *_moh_class_malloc(const char *file, int line, const char
)) {
class->format = ao2_bump(ast_format_slin);
class->srcfd = -1;
+ class->kill_delay = 100000;
}
return class;
@@ -1600,44 +1634,22 @@ static void moh_class_destructor(void *obj)
if (class->pid > 1) {
char buff[8192];
- int bytes, tbytes = 0, stime = 0, pid = 0;
+ int bytes, tbytes = 0, stime = 0;
ast_debug(1, "killing %d!\n", class->pid);
stime = time(NULL) + 2;
- pid = class->pid;
- class->pid = 0;
-
- /* Back when this was just mpg123, SIGKILL was fine. Now we need
- * to give the process a reason and time enough to kill off its
- * children. */
- do {
- if (killpg(pid, SIGHUP) < 0) {
- ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
- }
- usleep(100000);
- if (killpg(pid, SIGTERM) < 0) {
- if (errno == ESRCH) {
- break;
- }
- ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
- }
- usleep(100000);
- if (killpg(pid, SIGKILL) < 0) {
- if (errno == ESRCH) {
- break;
- }
- ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
- }
- } while (0);
+ killpid(class->pid, class->kill_delay, class->kill_method);
while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
(bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
tbytes = tbytes + bytes;
}
- ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
+ ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n",
+ class->pid, tbytes);
+ class->pid = 0;
close(class->srcfd);
class->srcfd = -1;
}
@@ -1765,6 +1777,22 @@ static int load_moh_classes(int reload)
ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
class->format = ao2_bump(ast_format_slin);
}
+ } else if (!strcasecmp(var->name, "kill_escalation_delay")) {
+ if (sscanf(var->value, "%zu", &class->kill_delay) == 1) {
+ class->kill_delay *= 1000;
+ } else {
+ ast_log(LOG_WARNING, "kill_escalation_delay '%s' is invalid. Setting to 100ms\n", var->value);
+ class->kill_delay = 100000;
+ }
+ } else if (!strcasecmp(var->name, "kill_method")) {
+ if (!strcasecmp(var->value, "process")) {
+ class->kill_method = KILL_METHOD_PROCESS;
+ } else if (!strcasecmp(var->value, "process_group")){
+ class->kill_method = KILL_METHOD_PROCESS_GROUP;
+ } else {
+ ast_log(LOG_WARNING, "kill_method '%s' is invalid. Setting to 'process_group'\n", var->value);
+ class->kill_method = KILL_METHOD_PROCESS_GROUP;
+ }
}
}
@@ -1899,6 +1927,9 @@ static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struc
ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
if (ast_test_flag(class, MOH_CUSTOM)) {
ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
+ ast_cli(a->fd, "\tKill Escalation Delay: %zu ms\n", class->kill_delay / 1000);
+ ast_cli(a->fd, "\tKill Method: %s\n",
+ class->kill_method == KILL_METHOD_PROCESS ? "process" : "process_group");
}
if (strcasecmp(class->mode, "files")) {
ast_cli(a->fd, "\tFormat: %s\n", ast_format_get_name(class->format));
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 6f1c19e08..2db0668c3 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -193,11 +193,18 @@
<description>
<para>Method used when updating connected line information.</para>
<enumlist>
- <enum name="invite" />
+ <enum name="invite">
+ <para>When set to <literal>invite</literal>, check the remote's Allow header and
+ if UPDATE is allowed, send UPDATE instead of INVITE to avoid SDP
+ renegotiation. If UPDATE is not Allowed, send INVITE.</para>
+ </enum>
<enum name="reinvite">
<para>Alias for the <literal>invite</literal> value.</para>
</enum>
- <enum name="update" />
+ <enum name="update">
+ <para>If set to <literal>update</literal>, send UPDATE regardless of what the remote
+ Allows. </para>
+ </enum>
</enumlist>
</description>
</configOption>
@@ -229,6 +236,9 @@
<enum name="auto">
<para>DTMF is sent as RFC 4733 if the other side supports it or as INBAND if not.</para>
</enum>
+ <enum name="auto_info">
+ <para>DTMF is sent as RFC 4733 if the other side supports it or as SIP INFO if not.</para>
+ </enum>
</enumlist>
</description>
</configOption>
@@ -357,9 +367,12 @@
<configOption name="rewrite_contact">
<synopsis>Allow Contact header to be rewritten with the source IP address-port</synopsis>
<description><para>
- On inbound SIP messages from this endpoint, the Contact header or an appropriate Record-Route
- header will be changed to have the source IP address and port. This option does not affect
- outbound messages sent to this endpoint.
+ On inbound SIP messages from this endpoint, the Contact header or an
+ appropriate Record-Route header will be changed to have the source IP
+ address and port. This option does not affect outbound messages sent to
+ this endpoint. This option helps servers communicate with endpoints
+ that are behind NATs. This option also helps reuse reliable transport
+ connections such as TCP and TLS.
</para></description>
</configOption>
<configOption name="rtp_ipv6" default="no">
@@ -965,6 +978,13 @@
will not send the progress details, but immediately will send "200 OK".
</para></description>
</configOption>
+ <configOption name="notify_early_inuse_ringing" default="no">
+ <synopsis>Whether to notifies dialog-info 'early' on InUse&amp;Ringing state</synopsis>
+ <description><para>
+ Control whether dialog-info subscriptions get 'early' state
+ on Ringing when already INUSE.
+ </para></description>
+ </configOption>
</configObject>
<configObject name="auth">
<synopsis>Authentication type</synopsis>
@@ -1307,6 +1327,13 @@
in incoming SIP REGISTER requests and is not intended to be configured manually.
</para></description>
</configOption>
+ <configOption name="prune_on_boot">
+ <synopsis>A contact that cannot survive a restart/boot.</synopsis>
+ <description><para>
+ The option is set if the incoming SIP REGISTER contact is rewritten
+ on a reliable transport and is not intended to be configured manually.
+ </para></description>
+ </configOption>
</configObject>
<configObject name="aor">
<synopsis>The configuration for a location of an endpoint</synopsis>
@@ -3058,6 +3085,14 @@ pjsip_dialog *ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint,
/* Update the dialog with the new local URI, we do it afterwards so we can use the dialog pool for construction */
pj_strdup_with_null(dlg->pool, &dlg->local.info_str, &local_uri);
dlg->local.info->uri = pjsip_parse_uri(dlg->pool, dlg->local.info_str.ptr, dlg->local.info_str.slen, 0);
+ if (!dlg->local.info->uri) {
+ ast_log(LOG_ERROR,
+ "Could not parse URI '%s' for endpoint '%s'\n",
+ dlg->local.info_str.ptr, ast_sorcery_object_get_id(endpoint));
+ dlg->sess_count--;
+ pjsip_dlg_terminate(dlg);
+ return NULL;
+ }
dlg->local.contact = pjsip_parse_hdr(dlg->pool, &HCONTACT, local_uri.ptr, local_uri.slen, NULL);
@@ -4372,6 +4407,56 @@ const char *ast_sip_get_host_ip_string(int af)
return NULL;
}
+int ast_sip_dtmf_to_str(const enum ast_sip_dtmf_mode dtmf,
+ char *buf, size_t buf_len)
+{
+ switch (dtmf) {
+ case AST_SIP_DTMF_NONE:
+ ast_copy_string(buf, "none", buf_len);
+ break;
+ case AST_SIP_DTMF_RFC_4733:
+ ast_copy_string(buf, "rfc4733", buf_len);
+ break;
+ case AST_SIP_DTMF_INBAND:
+ ast_copy_string(buf, "inband", buf_len);
+ break;
+ case AST_SIP_DTMF_INFO:
+ ast_copy_string(buf, "info", buf_len);
+ break;
+ case AST_SIP_DTMF_AUTO:
+ ast_copy_string(buf, "auto", buf_len);
+ break;
+ case AST_SIP_DTMF_AUTO_INFO:
+ ast_copy_string(buf, "auto_info", buf_len);
+ break;
+ default:
+ buf[0] = '\0';
+ return -1;
+ }
+ return 0;
+}
+
+int ast_sip_str_to_dtmf(const char * dtmf_mode)
+{
+ int result = -1;
+
+ if (!strcasecmp(dtmf_mode, "info")) {
+ result = AST_SIP_DTMF_INFO;
+ } else if (!strcasecmp(dtmf_mode, "rfc4733")) {
+ result = AST_SIP_DTMF_RFC_4733;
+ } else if (!strcasecmp(dtmf_mode, "inband")) {
+ result = AST_SIP_DTMF_INBAND;
+ } else if (!strcasecmp(dtmf_mode, "none")) {
+ result = AST_SIP_DTMF_NONE;
+ } else if (!strcasecmp(dtmf_mode, "auto")) {
+ result = AST_SIP_DTMF_AUTO;
+ } else if (!strcasecmp(dtmf_mode, "auto_info")) {
+ result = AST_SIP_DTMF_AUTO_INFO;
+ }
+
+ return result;
+}
+
/*!
* \brief Set name and number information on an identity header.
*
@@ -4502,6 +4587,7 @@ static int unload_pjsip(void *data)
ast_sip_destroy_system();
ast_sip_destroy_global_headers();
internal_sip_unregister_service(&supplement_module);
+ ast_sip_destroy_transport_events();
}
if (monitor_thread) {
@@ -4580,7 +4666,6 @@ static int load_pjsip(void)
return AST_MODULE_LOAD_SUCCESS;
error:
- unload_pjsip(NULL);
return AST_MODULE_LOAD_DECLINE;
}
@@ -4646,6 +4731,11 @@ static int load_module(void)
goto error;
}
+ if (ast_sip_initialize_transport_events()) {
+ ast_log(LOG_ERROR, "Failed to initialize SIP transport monitor. Aborting load\n");
+ goto error;
+ }
+
ast_sip_initialize_dns();
ast_sip_initialize_global_headers();
diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c
index 62bc9d67d..0c804b82a 100644
--- a/res/res_pjsip/config_transport.c
+++ b/res/res_pjsip/config_transport.c
@@ -248,8 +248,11 @@ static int destroy_sip_transport_state(void *data)
ast_free(transport_state->id);
ast_free_ha(transport_state->localnet);
- if (transport_state->external_address_refresher) {
- ast_dnsmgr_release(transport_state->external_address_refresher);
+ if (transport_state->external_signaling_address_refresher) {
+ ast_dnsmgr_release(transport_state->external_signaling_address_refresher);
+ }
+ if (transport_state->external_media_address_refresher) {
+ ast_dnsmgr_release(transport_state->external_media_address_refresher);
}
if (transport_state->transport) {
pjsip_transport_shutdown(transport_state->transport);
@@ -399,8 +402,8 @@ static void copy_state_to_transport(struct ast_sip_transport *transport)
memcpy(&transport->tls, &transport->state->tls, sizeof(transport->tls));
memcpy(&transport->ciphers, &transport->state->ciphers, sizeof(transport->ciphers));
transport->localnet = transport->state->localnet;
- transport->external_address_refresher = transport->state->external_address_refresher;
- memcpy(&transport->external_address, &transport->state->external_address, sizeof(transport->external_address));
+ transport->external_address_refresher = transport->state->external_signaling_address_refresher;
+ memcpy(&transport->external_address, &transport->state->external_signaling_address, sizeof(transport->external_signaling_address));
}
static int has_state_changed(struct ast_sip_transport_state *a, struct ast_sip_transport_state *b)
@@ -421,7 +424,11 @@ static int has_state_changed(struct ast_sip_transport_state *a, struct ast_sip_t
return -1;
}
- if (ast_sockaddr_cmp(&a->external_address, &b->external_address)) {
+ if (ast_sockaddr_cmp(&a->external_signaling_address, &b->external_signaling_address)) {
+ return -1;
+ }
+
+ if (ast_sockaddr_cmp(&a->external_media_address, &b->external_media_address)) {
return -1;
}
@@ -515,24 +522,41 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
pj_sockaddr_set_port(&temp_state->state->host, (transport->type == AST_TRANSPORT_TLS) ? 5061 : 5060);
}
- /* Now that we know what address family we can set up a dnsmgr refresh for the external media address if present */
+ /* Now that we know what address family we can set up a dnsmgr refresh for the external addresses if present */
if (!ast_strlen_zero(transport->external_signaling_address)) {
if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
- temp_state->state->external_address.ss.ss_family = AF_INET;
+ temp_state->state->external_signaling_address.ss.ss_family = AF_INET;
} else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) {
- temp_state->state->external_address.ss.ss_family = AF_INET6;
+ temp_state->state->external_signaling_address.ss.ss_family = AF_INET6;
} else {
ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external signaling address\n",
transport_id);
return -1;
}
- if (ast_dnsmgr_lookup(transport->external_signaling_address, &temp_state->state->external_address, &temp_state->state->external_address_refresher, NULL) < 0) {
+ if (ast_dnsmgr_lookup(transport->external_signaling_address, &temp_state->state->external_signaling_address, &temp_state->state->external_signaling_address_refresher, NULL) < 0) {
ast_log(LOG_ERROR, "Could not create dnsmgr for external signaling address on '%s'\n", transport_id);
return -1;
}
}
+ if (!ast_strlen_zero(transport->external_media_address)) {
+ if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
+ temp_state->state->external_media_address.ss.ss_family = AF_INET;
+ } else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) {
+ temp_state->state->external_media_address.ss.ss_family = AF_INET6;
+ } else {
+ ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external media address\n",
+ transport_id);
+ return -1;
+ }
+
+ if (ast_dnsmgr_lookup(transport->external_media_address, &temp_state->state->external_media_address, &temp_state->state->external_media_address_refresher, NULL) < 0) {
+ ast_log(LOG_ERROR, "Could not create dnsmgr for external media address on '%s'\n", transport_id);
+ return -1;
+ }
+ }
+
if (transport->type == AST_TRANSPORT_UDP) {
for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
@@ -1103,7 +1127,9 @@ static int transport_localnet_handler(const struct aco_option *opt, struct ast_v
return 0;
}
- if (!(state->localnet = ast_append_ha("d", var->value, state->localnet, &error))) {
+ /* We use only the ast_apply_ha() which defaults to ALLOW
+ * ("permit"), so we add DENY rules. */
+ if (!(state->localnet = ast_append_ha("deny", var->value, state->localnet, &error))) {
return -1;
}
diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h
index 11ad12c45..5766325b2 100644
--- a/res/res_pjsip/include/res_pjsip_private.h
+++ b/res/res_pjsip/include/res_pjsip_private.h
@@ -135,6 +135,29 @@ void ast_sip_destroy_distributor(void);
/*!
* \internal
+ * \brief Initialize the transport events notify module
+ * \since 13.18.0
+ *
+ * The transport events notify module is responsible for monitoring
+ * when transports die and calling any registered callbacks when that
+ * happens. It also manages any PJPROJECT transport state callbacks
+ * registered to it so the callbacks be more dynamic allowing module
+ * loading/unloading.
+ *
+ * \retval -1 Failure
+ * \retval 0 Success
+ */
+int ast_sip_initialize_transport_events(void);
+
+/*!
+ * \internal
+ * \brief Destruct the transport events notify module.
+ * \since 13.18.0
+ */
+void ast_sip_destroy_transport_events(void);
+
+/*!
+ * \internal
* \brief Initialize global type on a sorcery instance
*
* \retval -1 failure
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index 05e19f53a..ddde5c47e 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -356,13 +356,12 @@ struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_na
return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "contact", contact_name);
}
-int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri,
- struct timeval expiration_time, const char *path_info, const char *user_agent,
- const char *via_addr, int via_port, const char *call_id,
- struct ast_sip_endpoint *endpoint)
+struct ast_sip_contact *ast_sip_location_create_contact(struct ast_sip_aor *aor,
+ const char *uri, struct timeval expiration_time, const char *path_info,
+ const char *user_agent, const char *via_addr, int via_port, const char *call_id,
+ int prune_on_boot, struct ast_sip_endpoint *endpoint)
{
struct ast_sip_contact *contact;
- int res;
char name[MAX_OBJECT_FIELD * 2 + 3];
char hash[33];
@@ -371,7 +370,7 @@ int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri
contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", name);
if (!contact) {
- return -1;
+ return NULL;
}
ast_string_field_set(contact, uri, uri);
@@ -405,14 +404,30 @@ int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri
}
contact->endpoint = ao2_bump(endpoint);
-
if (endpoint) {
ast_string_field_set(contact, endpoint_name, ast_sorcery_object_get_id(endpoint));
}
- res = ast_sorcery_create(ast_sip_get_sorcery(), contact);
- ao2_ref(contact, -1);
- return res;
+ contact->prune_on_boot = prune_on_boot;
+
+ if (ast_sorcery_create(ast_sip_get_sorcery(), contact)) {
+ ao2_ref(contact, -1);
+ return NULL;
+ }
+ return contact;
+}
+
+int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri,
+ struct timeval expiration_time, const char *path_info, const char *user_agent,
+ const char *via_addr, int via_port, const char *call_id,
+ struct ast_sip_endpoint *endpoint)
+{
+ struct ast_sip_contact *contact;
+
+ contact = ast_sip_location_create_contact(aor, uri, expiration_time, path_info,
+ user_agent, via_addr, via_port, call_id, 0, endpoint);
+ ao2_cleanup(contact);
+ return contact ? 0 : -1;
}
int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
@@ -448,6 +463,32 @@ int ast_sip_location_delete_contact(struct ast_sip_contact *contact)
return ast_sorcery_delete(ast_sip_get_sorcery(), contact);
}
+static int prune_boot_contacts_cb(void *obj, void *arg, int flags)
+{
+ struct ast_sip_contact *contact = obj;
+
+ if (contact->prune_on_boot
+ && !strcmp(contact->reg_server, ast_config_AST_SYSTEM_NAME ?: "")) {
+ ast_verb(3, "Removed contact '%s' from AOR '%s' due to system boot\n",
+ contact->uri, contact->aor);
+ ast_sip_location_delete_contact(contact);
+ }
+
+ return 0;
+}
+
+void ast_sip_location_prune_boot_contacts(void)
+{
+ struct ao2_container *contacts;
+
+ contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "contact",
+ AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+ if (contacts) {
+ ao2_callback(contacts, 0, prune_boot_contacts_cb, NULL);
+ ao2_ref(contacts, -1);
+ }
+}
+
/*! \brief Custom handler for translating from a string timeval to actual structure */
static int expiration_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
{
@@ -1228,6 +1269,7 @@ int ast_sip_initialize_sorcery_location(void)
ast_sorcery_object_field_register(sorcery, "contact", "via_addr", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, via_addr));
ast_sorcery_object_field_register(sorcery, "contact", "via_port", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_contact, via_port));
ast_sorcery_object_field_register(sorcery, "contact", "call_id", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, call_id));
+ ast_sorcery_object_field_register(sorcery, "contact", "prune_on_boot", "no", OPT_YESNO_T, 1, FLDSET(struct ast_sip_contact, prune_on_boot));
ast_sorcery_object_field_register(sorcery, "aor", "type", "", OPT_NOOP_T, 0, 0);
ast_sorcery_object_field_register(sorcery, "aor", "minimum_expiration", "60", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, minimum_expiration));
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 77e31abe5..27dadb178 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -368,42 +368,29 @@ static int contact_acl_to_str(const void *obj, const intptr_t *args, char **buf)
static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
{
struct ast_sip_endpoint *endpoint = obj;
+ enum ast_sip_dtmf_mode dtmf = ast_sip_str_to_dtmf(var->value);
- if (!strcasecmp(var->value, "rfc4733")) {
- endpoint->dtmf = AST_SIP_DTMF_RFC_4733;
- } else if (!strcasecmp(var->value, "inband")) {
- endpoint->dtmf = AST_SIP_DTMF_INBAND;
- } else if (!strcasecmp(var->value, "info")) {
- endpoint->dtmf = AST_SIP_DTMF_INFO;
- } else if (!strcasecmp(var->value, "auto")) {
- endpoint->dtmf = AST_SIP_DTMF_AUTO;
- } else if (!strcasecmp(var->value, "none")) {
- endpoint->dtmf = AST_SIP_DTMF_NONE;
- } else {
+ if (dtmf == -1) {
return -1;
}
+ endpoint->dtmf = dtmf;
return 0;
}
static int dtmf_to_str(const void *obj, const intptr_t *args, char **buf)
{
const struct ast_sip_endpoint *endpoint = obj;
+ char dtmf_str[20];
+ int result = -1;
- switch (endpoint->dtmf) {
- case AST_SIP_DTMF_RFC_4733 :
- *buf = "rfc4733"; break;
- case AST_SIP_DTMF_INBAND :
- *buf = "inband"; break;
- case AST_SIP_DTMF_INFO :
- *buf = "info"; break;
- case AST_SIP_DTMF_AUTO :
- *buf = "auto"; break;
- default:
- *buf = "none";
- }
+ result = ast_sip_dtmf_to_str(endpoint->dtmf, dtmf_str, sizeof(dtmf_str));
- *buf = ast_strdup(*buf);
+ if (result == 0) {
+ *buf = ast_strdup(dtmf_str);
+ } else {
+ *buf = ast_strdup("none");
+ }
return 0;
}
@@ -1145,6 +1132,37 @@ static int tos_video_to_str(const void *obj, const intptr_t *args, char **buf)
return 0;
}
+static int from_user_handler(const struct aco_option *opt,
+ struct ast_variable *var, void *obj)
+{
+ struct ast_sip_endpoint *endpoint = obj;
+ /* Valid non-alphanumeric characters for URI */
+ char *valid_uri_marks = "-_.!~*`()";
+ const char *val;
+
+ for (val = var->value; *val; val++) {
+ if (!strchr(valid_uri_marks, *val) && !isdigit(*val) && !isalpha(*val)) {
+ ast_log(LOG_ERROR, "Error configuring endpoint '%s' - '%s' field "
+ "contains invalid character '%c'\n",
+ ast_sorcery_object_get_id(endpoint), var->name, *val);
+ return -1;
+ }
+ }
+
+ ast_string_field_set(endpoint, fromuser, var->value);
+
+ return 0;
+}
+
+static int from_user_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+ const struct ast_sip_endpoint *endpoint = obj;
+
+ *buf = ast_strdup(endpoint->fromuser);
+
+ return 0;
+}
+
static int set_var_handler(const struct aco_option *opt,
struct ast_variable *var, void *obj)
{
@@ -1909,7 +1927,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "cos_video", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.cos_video));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_subscribe", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, subscription.allow));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "sub_min_expiry", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, subscription.minexpiry));
- ast_sorcery_object_field_register(sip_sorcery, "endpoint", "from_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, fromuser));
+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "from_user", "", from_user_handler, from_user_to_str, NULL, 0, 0);
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "from_domain", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, fromdomain));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mwi_from_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, subscription.mwi.fromuser));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_engine", "asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.rtp.engine));
@@ -1941,6 +1959,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtcp_mux", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, rtcp_mux));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_overlap", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, allow_overlap));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "refer_blind_progress", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, refer_blind_progress));
+ ast_sorcery_object_field_register(sip_sorcery, "endpoint", "notify_early_inuse_ringing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, notify_early_inuse_ringing));
if (ast_sip_initialize_sorcery_transport()) {
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
@@ -2003,6 +2022,8 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
load_all_endpoints();
+ ast_sip_location_prune_boot_contacts();
+
return 0;
}
diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c
index dadde2577..3f245eea0 100644
--- a/res/res_pjsip/pjsip_distributor.c
+++ b/res/res_pjsip/pjsip_distributor.c
@@ -140,62 +140,189 @@ static struct ast_taskprocessor *find_request_serializer(pjsip_rx_data *rdata)
/*! Dialog-specific information the distributor uses */
struct distributor_dialog_data {
+ /*! dialog_associations ao2 container key */
+ pjsip_dialog *dlg;
/*! Serializer to distribute tasks to for this dialog */
struct ast_taskprocessor *serializer;
/*! Endpoint associated with this dialog */
struct ast_sip_endpoint *endpoint;
};
+#define DIALOG_ASSOCIATIONS_BUCKETS 251
+
+static struct ao2_container *dialog_associations;
+
/*!
* \internal
+ * \brief Compute a hash value on an arbitrary buffer.
+ * \since 13.17.0
*
- * \note Call this with the dialog locked
+ * \param[in] pos The buffer to add to the hash
+ * \param[in] len The buffer length to add to the hash
+ * \param[in] hash The hash value to add to
+ *
+ * \details
+ * This version of the function is for when you need to compute a
+ * hash of more than one buffer.
+ *
+ * This famous hash algorithm was written by Dan Bernstein and is
+ * commonly used.
+ *
+ * \sa http://www.cse.yorku.ca/~oz/hash.html
*/
-static struct distributor_dialog_data *distributor_dialog_data_alloc(pjsip_dialog *dlg)
+static int buf_hash_add(const char *pos, size_t len, int hash)
{
- struct distributor_dialog_data *dist;
+ while (len--) {
+ hash = hash * 33 ^ *pos++;
+ }
+
+ return hash;
+}
+
+/*!
+ * \internal
+ * \brief Compute a hash value on an arbitrary buffer.
+ * \since 13.17.0
+ *
+ * \param[in] pos The buffer to add to the hash
+ * \param[in] len The buffer length to add to the hash
+ *
+ * \details
+ * This version of the function is for when you need to compute a
+ * hash of more than one buffer.
+ *
+ * This famous hash algorithm was written by Dan Bernstein and is
+ * commonly used.
+ *
+ * \sa http://www.cse.yorku.ca/~oz/hash.html
+ */
+static int buf_hash(const char *pos, size_t len)
+{
+ return buf_hash_add(pos, len, 5381);
+}
- dist = PJ_POOL_ZALLOC_T(dlg->pool, struct distributor_dialog_data);
- pjsip_dlg_set_mod_data(dlg, distributor_mod.id, dist);
+static int dialog_associations_hash(const void *obj, int flags)
+{
+ const struct distributor_dialog_data *object;
+ union {
+ const pjsip_dialog *dlg;
+ const char buf[sizeof(pjsip_dialog *)];
+ } key;
- return dist;
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_KEY:
+ key.dlg = obj;
+ break;
+ case OBJ_SEARCH_OBJECT:
+ object = obj;
+ key.dlg = object->dlg;
+ break;
+ default:
+ /* Hash can only work on something with a full key. */
+ ast_assert(0);
+ return 0;
+ }
+ return ast_str_hash_restrict(buf_hash(key.buf, sizeof(key.buf)));
+}
+
+static int dialog_associations_cmp(void *obj, void *arg, int flags)
+{
+ const struct distributor_dialog_data *object_left = obj;
+ const struct distributor_dialog_data *object_right = arg;
+ const pjsip_dialog *right_key = arg;
+ int cmp = 0;
+
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_OBJECT:
+ right_key = object_right->dlg;
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ if (object_left->dlg == right_key) {
+ cmp = CMP_MATCH;
+ }
+ break;
+ case OBJ_SEARCH_PARTIAL_KEY:
+ /* There is no such thing for this container. */
+ ast_assert(0);
+ break;
+ default:
+ cmp = 0;
+ break;
+ }
+ return cmp;
}
void ast_sip_dialog_set_serializer(pjsip_dialog *dlg, struct ast_taskprocessor *serializer)
{
struct distributor_dialog_data *dist;
- SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock);
- dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
+ ao2_wrlock(dialog_associations);
+ dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY | OBJ_NOLOCK);
if (!dist) {
- dist = distributor_dialog_data_alloc(dlg);
+ if (serializer) {
+ dist = ao2_alloc(sizeof(*dist), NULL);
+ if (dist) {
+ dist->dlg = dlg;
+ dist->serializer = serializer;
+ ao2_link_flags(dialog_associations, dist, OBJ_NOLOCK);
+ ao2_ref(dist, -1);
+ }
+ }
+ } else {
+ ao2_lock(dist);
+ dist->serializer = serializer;
+ if (!dist->serializer && !dist->endpoint) {
+ ao2_unlink_flags(dialog_associations, dist, OBJ_NOLOCK);
+ }
+ ao2_unlock(dist);
+ ao2_ref(dist, -1);
}
- dist->serializer = serializer;
+ ao2_unlock(dialog_associations);
}
void ast_sip_dialog_set_endpoint(pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint)
{
struct distributor_dialog_data *dist;
- SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock);
- dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
+ ao2_wrlock(dialog_associations);
+ dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY | OBJ_NOLOCK);
if (!dist) {
- dist = distributor_dialog_data_alloc(dlg);
+ if (endpoint) {
+ dist = ao2_alloc(sizeof(*dist), NULL);
+ if (dist) {
+ dist->dlg = dlg;
+ dist->endpoint = endpoint;
+ ao2_link_flags(dialog_associations, dist, OBJ_NOLOCK);
+ ao2_ref(dist, -1);
+ }
+ }
+ } else {
+ ao2_lock(dist);
+ dist->endpoint = endpoint;
+ if (!dist->serializer && !dist->endpoint) {
+ ao2_unlink_flags(dialog_associations, dist, OBJ_NOLOCK);
+ }
+ ao2_unlock(dist);
+ ao2_ref(dist, -1);
}
- dist->endpoint = endpoint;
+ ao2_unlock(dialog_associations);
}
struct ast_sip_endpoint *ast_sip_dialog_get_endpoint(pjsip_dialog *dlg)
{
struct distributor_dialog_data *dist;
- SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock);
+ struct ast_sip_endpoint *endpoint;
- dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
- if (!dist || !dist->endpoint) {
- return NULL;
+ dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY);
+ if (dist) {
+ ao2_lock(dist);
+ endpoint = ao2_bump(dist->endpoint);
+ ao2_unlock(dist);
+ ao2_ref(dist, -1);
+ } else {
+ endpoint = NULL;
}
- ao2_ref(dist->endpoint, +1);
- return dist->endpoint;
+ return endpoint;
}
static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
@@ -227,7 +354,7 @@ static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) ||
rdata->msg_info.to->tag.slen != 0) {
dlg = pjsip_ua_find_dialog(&rdata->msg_info.cid->id, local_tag,
- remote_tag, PJ_TRUE);
+ remote_tag, PJ_FALSE);
if (dlg) {
return dlg;
}
@@ -265,11 +392,6 @@ static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
pj_mutex_unlock(tsx->mutex);
#endif
- if (!dlg) {
- return NULL;
- }
-
- pjsip_dlg_inc_lock(dlg);
return dlg;
}
@@ -292,16 +414,7 @@ static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
*/
static int pjstr_hash_add(pj_str_t *str, int hash)
{
- size_t len;
- const char *pos;
-
- len = pj_strlen(str);
- pos = pj_strbuf(str);
- while (len--) {
- hash = hash * 33 ^ *pos++;
- }
-
- return hash;
+ return buf_hash_add(pj_strbuf(str), pj_strlen(str), hash);
}
/*!
@@ -340,7 +453,7 @@ struct ast_taskprocessor *ast_sip_get_distributor_serializer(pjsip_rx_data *rdat
/* Compute the hash from the SIP message call-id and remote-tag */
hash = pjstr_hash(&rdata->msg_info.cid->id);
hash = pjstr_hash_add(remote_tag, hash);
- hash = abs(hash);
+ hash = ast_str_hash_restrict(hash);
serializer = ao2_bump(distributor_pool[hash % ARRAY_LEN(distributor_pool)]);
if (serializer) {
@@ -375,17 +488,18 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
dlg = find_dialog(rdata);
if (dlg) {
- ast_debug(3, "Searching for serializer on dialog %s for %s\n",
+ ast_debug(3, "Searching for serializer associated with dialog %s for %s\n",
dlg->obj_name, pjsip_rx_data_get_info(rdata));
- dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
+ dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY);
if (dist) {
+ ao2_lock(dist);
serializer = ao2_bump(dist->serializer);
+ ao2_unlock(dist);
if (serializer) {
- ast_debug(3, "Found serializer %s on dialog %s\n",
+ ast_debug(3, "Found serializer %s associated with dialog %s\n",
ast_taskprocessor_name(serializer), dlg->obj_name);
}
}
- pjsip_dlg_dec_lock(dlg);
}
if (serializer) {
@@ -407,6 +521,7 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
/* We have a BYE or CANCEL request without a serializer. */
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata,
PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, NULL, NULL, NULL);
+ ao2_cleanup(dist);
return PJ_TRUE;
} else {
if (ast_taskprocessor_alert_get()) {
@@ -421,6 +536,7 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
*/
ast_debug(3, "Taskprocessor overload alert: Ignoring '%s'.\n",
pjsip_rx_data_get_info(rdata));
+ ao2_cleanup(dist);
return PJ_TRUE;
}
@@ -428,10 +544,17 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
serializer = ast_sip_get_distributor_serializer(rdata);
}
- pjsip_rx_data_clone(rdata, 0, &clone);
+ if (pjsip_rx_data_clone(rdata, 0, &clone) != PJ_SUCCESS) {
+ ast_taskprocessor_unreference(serializer);
+ ao2_cleanup(dist);
+ return PJ_TRUE;
+ }
if (dist) {
+ ao2_lock(dist);
clone->endpt_info.mod_data[endpoint_mod.id] = ao2_bump(dist->endpoint);
+ ao2_unlock(dist);
+ ao2_cleanup(dist);
}
if (ast_sip_push_task(serializer, distribute, clone)) {
@@ -827,7 +950,7 @@ static int suspects_compare(void *obj, void *arg, int flags)
/* Fall through */
case OBJ_SEARCH_KEY:
if (strcmp(object_left->src_name, right_key) == 0) {
- cmp = CMP_MATCH | CMP_STOP;
+ cmp = CMP_MATCH;
}
break;
case OBJ_SEARCH_PARTIAL_KEY:
@@ -842,15 +965,25 @@ static int suspects_compare(void *obj, void *arg, int flags)
return cmp;
}
-static int suspects_hash(const void *obj, int flags) {
- const struct unidentified_request *object_left = obj;
+static int suspects_hash(const void *obj, int flags)
+{
+ const struct unidentified_request *object;
+ const char *key;
- if (flags & OBJ_SEARCH_OBJECT) {
- return ast_str_hash(object_left->src_name);
- } else if (flags & OBJ_SEARCH_KEY) {
- return ast_str_hash(obj);
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_KEY:
+ key = obj;
+ break;
+ case OBJ_SEARCH_OBJECT:
+ object = obj;
+ key = object->src_name;
+ break;
+ default:
+ /* Hash can only work on something with a full key. */
+ ast_assert(0);
+ return 0;
}
- return -1;
+ return ast_str_hash(key);
}
static struct ao2_container *cli_unid_get_container(const char *regex)
@@ -1068,6 +1201,14 @@ int ast_sip_initialize_distributor(void)
return -1;
}
+ dialog_associations = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK, 0,
+ DIALOG_ASSOCIATIONS_BUCKETS, dialog_associations_hash, NULL,
+ dialog_associations_cmp);
+ if (!dialog_associations) {
+ ast_sip_destroy_distributor();
+ return -1;
+ }
+
if (distributor_pool_setup()) {
ast_sip_destroy_distributor();
return -1;
@@ -1146,5 +1287,6 @@ void ast_sip_destroy_distributor(void)
distributor_pool_shutdown();
+ ao2_cleanup(dialog_associations);
ao2_cleanup(unidentified_requests);
}
diff --git a/res/res_pjsip/pjsip_message_ip_updater.c b/res/res_pjsip/pjsip_message_ip_updater.c
index 2d074640a..099ecaa66 100644
--- a/res/res_pjsip/pjsip_message_ip_updater.c
+++ b/res/res_pjsip/pjsip_message_ip_updater.c
@@ -153,7 +153,16 @@ static int multihomed_rewrite_sdp(struct pjmedia_sdp_session *sdp)
return 0;
}
-static void sanitize_tdata(pjsip_tx_data *tdata)
+#define is_sip_uri(uri) \
+ (PJSIP_URI_SCHEME_IS_SIP(uri) || PJSIP_URI_SCHEME_IS_SIPS(uri))
+
+#ifdef AST_DEVMODE
+#define FUNC_ATTRS __attribute__ ((noinline))
+#else
+#define FUNC_ATTRS
+#endif
+
+static void FUNC_ATTRS sanitize_tdata(pjsip_tx_data *tdata)
{
static const pj_str_t x_name = { AST_SIP_X_AST_TXP, AST_SIP_X_AST_TXP_LEN };
pjsip_param *x_transport;
@@ -161,29 +170,50 @@ static void sanitize_tdata(pjsip_tx_data *tdata)
pjsip_fromto_hdr *fromto;
pjsip_contact_hdr *contact;
pjsip_hdr *hdr;
+#ifdef AST_DEVMODE
+ char hdrbuf[512];
+ int hdrbuf_len;
+#endif
if (tdata->msg->type == PJSIP_REQUEST_MSG) {
- uri = pjsip_uri_get_uri(tdata->msg->line.req.uri);
- x_transport = pjsip_param_find(&uri->other_param, &x_name);
- if (x_transport) {
- pj_list_erase(x_transport);
+ if (is_sip_uri(tdata->msg->line.req.uri)) {
+ uri = pjsip_uri_get_uri(tdata->msg->line.req.uri);
+#ifdef AST_DEVMODE
+ hdrbuf_len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, hdrbuf, 512);
+ ast_debug(2, "Sanitizing Request: %s\n", hdrbuf);
+#endif
+ while ((x_transport = pjsip_param_find(&uri->other_param, &x_name))) {
+ pj_list_erase(x_transport);
+ }
}
}
for (hdr = tdata->msg->hdr.next; hdr != &tdata->msg->hdr; hdr = hdr->next) {
if (hdr->type == PJSIP_H_TO || hdr->type == PJSIP_H_FROM) {
fromto = (pjsip_fromto_hdr *) hdr;
- uri = pjsip_uri_get_uri(fromto->uri);
- x_transport = pjsip_param_find(&uri->other_param, &x_name);
- if (x_transport) {
- pj_list_erase(x_transport);
+ if (is_sip_uri(fromto->uri)) {
+ uri = pjsip_uri_get_uri(fromto->uri);
+#ifdef AST_DEVMODE
+ hdrbuf_len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, uri, hdrbuf, 512);
+ hdrbuf[hdrbuf_len] = '\0';
+ ast_debug(2, "Sanitizing From/To: %s\n", hdrbuf);
+#endif
+ while ((x_transport = pjsip_param_find(&uri->other_param, &x_name))) {
+ pj_list_erase(x_transport);
+ }
}
} else if (hdr->type == PJSIP_H_CONTACT) {
contact = (pjsip_contact_hdr *) hdr;
- uri = pjsip_uri_get_uri(contact->uri);
- x_transport = pjsip_param_find(&uri->other_param, &x_name);
- if (x_transport) {
- pj_list_erase(x_transport);
+ if (is_sip_uri(contact->uri)) {
+ uri = pjsip_uri_get_uri(contact->uri);
+#ifdef AST_DEVMODE
+ hdrbuf_len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, uri, hdrbuf, 512);
+ hdrbuf[hdrbuf_len] = '\0';
+ ast_debug(2, "Sanitizing Contact: %s\n", hdrbuf);
+#endif
+ while ((x_transport = pjsip_param_find(&uri->other_param, &x_name))) {
+ pj_list_erase(x_transport);
+ }
}
}
}
diff --git a/res/res_pjsip/pjsip_transport_events.c b/res/res_pjsip/pjsip_transport_events.c
new file mode 100644
index 000000000..0f57303ba
--- /dev/null
+++ b/res/res_pjsip/pjsip_transport_events.c
@@ -0,0 +1,366 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2017, Digium Inc.
+ *
+ * Richard Mudgett <rmudgett@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Manages the global transport event notification callbacks.
+ *
+ * \author Richard Mudgett <rmudgett@digium.com>
+ * See Also:
+ *
+ * \arg \ref AstCREDITS
+ */
+
+
+#include "asterisk.h"
+
+#include "asterisk/res_pjsip.h"
+#include "include/res_pjsip_private.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/vector.h"
+
+/* ------------------------------------------------------------------- */
+
+/*! \brief Number of buckets for monitored active transports */
+#define ACTIVE_TRANSPORTS_BUCKETS 127
+
+/*! Who to notify when transport shuts down. */
+struct transport_monitor_notifier {
+ /*! Who to call when transport shuts down. */
+ ast_transport_monitor_shutdown_cb cb;
+ /*! ao2 data object to pass to callback. */
+ void *data;
+};
+
+/*! \brief Structure for transport to be monitored */
+struct transport_monitor {
+ /*! \brief The underlying PJSIP transport */
+ pjsip_transport *transport;
+ /*! Who is interested in when this transport shuts down. */
+ AST_VECTOR(, struct transport_monitor_notifier) monitors;
+};
+
+/*! \brief Global container of active reliable transports */
+static AO2_GLOBAL_OBJ_STATIC(active_transports);
+
+/*! \brief Existing transport events callback that we need to invoke */
+static pjsip_tp_state_callback tpmgr_state_callback;
+
+/*! List of registered transport state callbacks. */
+static AST_RWLIST_HEAD(, ast_sip_tpmgr_state_callback) transport_state_list;
+
+
+/*! \brief Hashing function for struct transport_monitor */
+AO2_STRING_FIELD_HASH_FN(transport_monitor, transport->obj_name);
+
+/*! \brief Comparison function for struct transport_monitor */
+AO2_STRING_FIELD_CMP_FN(transport_monitor, transport->obj_name);
+
+static const char *transport_state2str(pjsip_transport_state state)
+{
+ const char *name;
+
+ switch (state) {
+ case PJSIP_TP_STATE_CONNECTED:
+ name = "CONNECTED";
+ break;
+ case PJSIP_TP_STATE_DISCONNECTED:
+ name = "DISCONNECTED";
+ break;
+ case PJSIP_TP_STATE_SHUTDOWN:
+ name = "SHUTDOWN";
+ break;
+ case PJSIP_TP_STATE_DESTROY:
+ name = "DESTROY";
+ break;
+ default:
+ /*
+ * We have to have a default case because the enum is
+ * defined by a third-party library.
+ */
+ ast_assert(0);
+ name = "<unknown>";
+ break;
+ }
+ return name;
+}
+
+static void transport_monitor_dtor(void *vdoomed)
+{
+ struct transport_monitor *monitored = vdoomed;
+ int idx;
+
+ for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
+ struct transport_monitor_notifier *notifier;
+
+ notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
+ ao2_cleanup(notifier->data);
+ }
+ AST_VECTOR_FREE(&monitored->monitors);
+}
+
+/*! \brief Callback invoked when transport state changes occur */
+static void transport_state_callback(pjsip_transport *transport,
+ pjsip_transport_state state, const pjsip_transport_state_info *info)
+{
+ struct ao2_container *transports;
+
+ /* We only care about monitoring reliable transports */
+ if (PJSIP_TRANSPORT_IS_RELIABLE(transport)
+ && (transports = ao2_global_obj_ref(active_transports))) {
+ struct transport_monitor *monitored;
+
+ ast_debug(3, "Reliable transport '%s' state:%s\n",
+ transport->obj_name, transport_state2str(state));
+ switch (state) {
+ case PJSIP_TP_STATE_CONNECTED:
+ monitored = ao2_alloc_options(sizeof(*monitored),
+ transport_monitor_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!monitored) {
+ break;
+ }
+ monitored->transport = transport;
+ if (AST_VECTOR_INIT(&monitored->monitors, 2)) {
+ ao2_ref(monitored, -1);
+ break;
+ }
+
+ ao2_link(transports, monitored);
+ ao2_ref(monitored, -1);
+ break;
+ case PJSIP_TP_STATE_DISCONNECTED:
+ if (!transport->is_shutdown) {
+ pjsip_transport_shutdown(transport);
+ }
+ break;
+ case PJSIP_TP_STATE_SHUTDOWN:
+ /*
+ * Set shutdown flag early so we can force a new transport to be
+ * created if a monitor callback needs to reestablish a link.
+ * PJPROJECT sets the flag after this routine returns even though
+ * it has already called the transport's shutdown routine.
+ */
+ transport->is_shutdown = PJ_TRUE;
+
+ monitored = ao2_find(transports, transport->obj_name,
+ OBJ_SEARCH_KEY | OBJ_UNLINK);
+ if (monitored) {
+ int idx;
+
+ for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
+ struct transport_monitor_notifier *notifier;
+
+ notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
+ notifier->cb(notifier->data);
+ }
+ ao2_ref(monitored, -1);
+ }
+ break;
+ default:
+ break;
+ }
+
+ ao2_ref(transports, -1);
+ }
+
+ /* Loop over other transport state callbacks registered with us. */
+ if (!AST_LIST_EMPTY(&transport_state_list)) {
+ struct ast_sip_tpmgr_state_callback *tpmgr_notifier;
+
+ AST_RWLIST_RDLOCK(&transport_state_list);
+ AST_LIST_TRAVERSE(&transport_state_list, tpmgr_notifier, node) {
+ tpmgr_notifier->cb(transport, state, info);
+ }
+ AST_RWLIST_UNLOCK(&transport_state_list);
+ }
+
+ /* Forward to the old state callback if present */
+ if (tpmgr_state_callback) {
+ tpmgr_state_callback(transport, state, info);
+ }
+}
+
+static int transport_monitor_unregister_all(void *obj, void *arg, int flags)
+{
+ struct transport_monitor *monitored = obj;
+ ast_transport_monitor_shutdown_cb cb = arg;
+ int idx;
+
+ for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
+ struct transport_monitor_notifier *notifier;
+
+ notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
+ if (notifier->cb == cb) {
+ ao2_cleanup(notifier->data);
+ AST_VECTOR_REMOVE_UNORDERED(&monitored->monitors, idx);
+ break;
+ }
+ }
+ return 0;
+}
+
+void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb)
+{
+ struct ao2_container *transports;
+
+ transports = ao2_global_obj_ref(active_transports);
+ if (!transports) {
+ return;
+ }
+ ao2_callback(transports, OBJ_MULTIPLE | OBJ_NODATA, transport_monitor_unregister_all,
+ cb);
+ ao2_ref(transports, -1);
+}
+
+void ast_sip_transport_monitor_unregister(pjsip_transport *transport, ast_transport_monitor_shutdown_cb cb)
+{
+ struct ao2_container *transports;
+ struct transport_monitor *monitored;
+
+ transports = ao2_global_obj_ref(active_transports);
+ if (!transports) {
+ return;
+ }
+
+ ao2_lock(transports);
+ monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ if (monitored) {
+ int idx;
+
+ for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
+ struct transport_monitor_notifier *notifier;
+
+ notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
+ if (notifier->cb == cb) {
+ ao2_cleanup(notifier->data);
+ AST_VECTOR_REMOVE_UNORDERED(&monitored->monitors, idx);
+ break;
+ }
+ }
+ ao2_ref(monitored, -1);
+ }
+ ao2_unlock(transports);
+ ao2_ref(transports, -1);
+}
+
+enum ast_transport_monitor_reg ast_sip_transport_monitor_register(pjsip_transport *transport,
+ ast_transport_monitor_shutdown_cb cb, void *ao2_data)
+{
+ struct ao2_container *transports;
+ struct transport_monitor *monitored;
+ enum ast_transport_monitor_reg res = AST_TRANSPORT_MONITOR_REG_NOT_FOUND;
+
+ transports = ao2_global_obj_ref(active_transports);
+ if (!transports) {
+ return res;
+ }
+
+ ao2_lock(transports);
+ monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ if (monitored) {
+ int idx;
+ struct transport_monitor_notifier new_monitor;
+
+ /* Check if the callback monitor already exists */
+ for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
+ struct transport_monitor_notifier *notifier;
+
+ notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
+ if (notifier->cb == cb) {
+ /* The monitor is already in the vector replace with new ao2_data. */
+ ao2_replace(notifier->data, ao2_data);
+ res = AST_TRANSPORT_MONITOR_REG_REPLACED;
+ goto register_done;
+ }
+ }
+
+ /* Add new monitor to vector */
+ new_monitor.cb = cb;
+ new_monitor.data = ao2_bump(ao2_data);
+ if (AST_VECTOR_APPEND(&monitored->monitors, new_monitor)) {
+ ao2_cleanup(ao2_data);
+ res = AST_TRANSPORT_MONITOR_REG_FAILED;
+ }
+
+register_done:
+ ao2_ref(monitored, -1);
+ }
+ ao2_unlock(transports);
+ ao2_ref(transports, -1);
+ return res;
+}
+
+void ast_sip_transport_state_unregister(struct ast_sip_tpmgr_state_callback *element)
+{
+ AST_RWLIST_WRLOCK(&transport_state_list);
+ AST_LIST_REMOVE(&transport_state_list, element, node);
+ AST_RWLIST_UNLOCK(&transport_state_list);
+}
+
+void ast_sip_transport_state_register(struct ast_sip_tpmgr_state_callback *element)
+{
+ struct ast_sip_tpmgr_state_callback *tpmgr_notifier;
+
+ AST_RWLIST_WRLOCK(&transport_state_list);
+ AST_LIST_TRAVERSE(&transport_state_list, tpmgr_notifier, node) {
+ if (element == tpmgr_notifier) {
+ /* Already registered. */
+ AST_RWLIST_UNLOCK(&transport_state_list);
+ return;
+ }
+ }
+ AST_LIST_INSERT_HEAD(&transport_state_list, element, node);
+ AST_RWLIST_UNLOCK(&transport_state_list);
+}
+
+void ast_sip_destroy_transport_events(void)
+{
+ pjsip_tpmgr *tpmgr;
+
+ tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
+ if (tpmgr) {
+ pjsip_tpmgr_set_state_cb(tpmgr, tpmgr_state_callback);
+ }
+
+ ao2_global_obj_release(active_transports);
+}
+
+int ast_sip_initialize_transport_events(void)
+{
+ pjsip_tpmgr *tpmgr;
+ struct ao2_container *transports;
+
+ tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
+ if (!tpmgr) {
+ return -1;
+ }
+
+ transports = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
+ ACTIVE_TRANSPORTS_BUCKETS, transport_monitor_hash_fn, NULL,
+ transport_monitor_cmp_fn);
+ if (!transports) {
+ return -1;
+ }
+ ao2_global_obj_replace_unref(active_transports, transports);
+ ao2_ref(transports, -1);
+
+ tpmgr_state_callback = pjsip_tpmgr_get_state_cb(tpmgr);
+ pjsip_tpmgr_set_state_cb(tpmgr, &transport_state_callback);
+
+ return 0;
+}
diff --git a/res/res_pjsip/presence_xml.c b/res/res_pjsip/presence_xml.c
index c991a0d68..3cea79e98 100644
--- a/res/res_pjsip/presence_xml.c
+++ b/res/res_pjsip/presence_xml.c
@@ -82,43 +82,48 @@ void ast_sip_sanitize_xml(const char *input, char *output, size_t len)
}
void ast_sip_presence_exten_state_to_str(int state, char **statestring, char **pidfstate,
- char **pidfnote, enum ast_sip_pidf_state *local_state)
+ char **pidfnote, enum ast_sip_pidf_state *local_state,
+ unsigned int notify_early_inuse_ringing)
{
switch (state) {
case AST_EXTENSION_RINGING:
*statestring = "early";
*local_state = NOTIFY_INUSE;
- *pidfstate = "busy";
+ *pidfstate = "on-the-phone";
*pidfnote = "Ringing";
break;
case (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING):
- *statestring = "confirmed";
+ if (notify_early_inuse_ringing) {
+ *statestring = "early";
+ } else {
+ *statestring = "confirmed";
+ }
*local_state = NOTIFY_INUSE;
- *pidfstate = "busy";
+ *pidfstate = "on-the-phone";
*pidfnote = "Ringing";
break;
case AST_EXTENSION_INUSE:
*statestring = "confirmed";
*local_state = NOTIFY_INUSE;
- *pidfstate = "busy";
+ *pidfstate = "on-the-phone";
*pidfnote = "On the phone";
break;
case AST_EXTENSION_BUSY:
*statestring = "confirmed";
- *local_state = NOTIFY_CLOSED;
- *pidfstate = "busy";
+ *local_state = NOTIFY_INUSE;
+ *pidfstate = "on-the-phone";
*pidfnote = "On the phone";
break;
case AST_EXTENSION_UNAVAILABLE:
*statestring = "terminated";
*local_state = NOTIFY_CLOSED;
- *pidfstate = "away";
+ *pidfstate = "--";
*pidfnote = "Unavailable";
break;
case AST_EXTENSION_ONHOLD:
*statestring = "confirmed";
- *local_state = NOTIFY_CLOSED;
- *pidfstate = "busy";
+ *local_state = NOTIFY_INUSE;
+ *pidfstate = "on-the-phone";
*pidfnote = "On hold";
break;
case AST_EXTENSION_NOT_INUSE:
diff --git a/res/res_pjsip_dialog_info_body_generator.c b/res/res_pjsip_dialog_info_body_generator.c
index b21b70fb1..fa3d710e5 100644
--- a/res/res_pjsip_dialog_info_body_generator.c
+++ b/res/res_pjsip_dialog_info_body_generator.c
@@ -107,6 +107,8 @@ static int dialog_info_generate_body_content(void *body, void *data)
enum ast_sip_pidf_state local_state;
unsigned int version;
char version_str[32], sanitized[PJSIP_MAX_URL_SIZE];
+ struct ast_sip_endpoint *endpoint = NULL;
+ unsigned int notify_early_inuse_ringing = 0;
if (!local || !state_data->sub) {
return -1;
@@ -120,8 +122,12 @@ static int dialog_info_generate_body_content(void *body, void *data)
stripped = ast_strip_quoted(local, "<", ">");
ast_sip_sanitize_xml(stripped, sanitized, sizeof(sanitized));
+ if (state_data->sub && (endpoint = ast_sip_subscription_get_endpoint(state_data->sub))) {
+ notify_early_inuse_ringing = endpoint->notify_early_inuse_ringing;
+ ao2_cleanup(endpoint);
+ }
ast_sip_presence_exten_state_to_str(state_data->exten_state, &statestring,
- &pidfstate, &pidfnote, &local_state);
+ &pidfstate, &pidfnote, &local_state, notify_early_inuse_ringing);
ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "xmlns", "urn:ietf:params:xml:ns:dialog-info");
@@ -133,7 +139,7 @@ static int dialog_info_generate_body_content(void *body, void *data)
dialog = ast_sip_presence_xml_create_node(state_data->pool, dialog_info, "dialog");
ast_sip_presence_xml_create_attr(state_data->pool, dialog, "id", state_data->exten);
- if (state_data->exten_state == AST_EXTENSION_RINGING) {
+ if (!ast_strlen_zero(statestring) && !strcmp(statestring, "early")) {
ast_sip_presence_xml_create_attr(state_data->pool, dialog, "direction", "recipient");
}
diff --git a/res/res_pjsip_messaging.c b/res/res_pjsip_messaging.c
index 8b465e007..2f01a0f47 100644
--- a/res/res_pjsip_messaging.c
+++ b/res/res_pjsip_messaging.c
@@ -512,7 +512,7 @@ static enum pjsip_status_code rx_data_to_ast_msg(pjsip_rx_data *rdata, struct as
buf[size] = '\0';
res |= ast_msg_set_from(msg, "%s", buf);
- field = pj_sockaddr_print(&rdata->pkt_info.src_addr, buf, sizeof(buf) - 1, 1);
+ field = pj_sockaddr_print(&rdata->pkt_info.src_addr, buf, sizeof(buf) - 1, 3);
res |= ast_msg_set_var(msg, "PJSIP_RECVADDR", field);
switch (rdata->tp_info.transport->key.type) {
diff --git a/res/res_pjsip_mwi.c b/res/res_pjsip_mwi.c
index 5ae2af5d3..05eee782e 100644
--- a/res/res_pjsip_mwi.c
+++ b/res/res_pjsip_mwi.c
@@ -1102,6 +1102,13 @@ static int create_mwi_subscriptions_for_endpoint(void *obj, void *arg, int flags
}
if (endpoint->subscription.mwi.aggregate) {
+ const char *endpoint_id = ast_sorcery_object_get_id(endpoint);
+
+ /* Check if subscription exists */
+ aggregate_sub = ao2_find(unsolicited_mwi, endpoint_id, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ if (aggregate_sub) {
+ return 0;
+ }
aggregate_sub = mwi_subscription_alloc(endpoint, 0, NULL);
if (!aggregate_sub) {
return 0;
@@ -1113,7 +1120,9 @@ static int create_mwi_subscriptions_for_endpoint(void *obj, void *arg, int flags
struct mwi_subscription *sub;
struct mwi_stasis_subscription *mwi_stasis_sub;
- if (ast_strlen_zero(mailbox)) {
+ /* check if subscription exists */
+ if (ast_strlen_zero(mailbox) ||
+ (!aggregate_sub && endpoint_receives_unsolicited_mwi_for_mailbox(endpoint, mailbox))) {
continue;
}
@@ -1189,31 +1198,79 @@ static int send_contact_notify(void *obj, void *arg, int flags)
return 0;
}
-/*! \brief Function called when a contact is updated */
-static void mwi_contact_updated(const void *object)
+/*! \brief Create mwi subscriptions and notify */
+static void mwi_contact_changed(const struct ast_sip_contact *contact)
{
- char *id = ast_strdupa(ast_sorcery_object_get_id(object)), *aor = NULL;
+ char *id = ast_strdupa(ast_sorcery_object_get_id(contact));
+ char *aor = NULL;
+ struct ast_sip_endpoint *endpoint = NULL;
- aor = strsep(&id, ";@");
+ if (contact->endpoint) {
+ endpoint = ao2_bump(contact->endpoint);
+ } else {
+ if (!ast_strlen_zero(contact->endpoint_name)) {
+ endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", contact->endpoint_name);
+ }
+ }
+ if (!endpoint || ast_strlen_zero(endpoint->subscription.mwi.mailboxes)) {
+ ao2_cleanup(endpoint);
+ return;
+ }
+
+ ao2_lock(unsolicited_mwi);
+ create_mwi_subscriptions_for_endpoint(endpoint, NULL, 0);
+ ao2_unlock(unsolicited_mwi);
+ ao2_cleanup(endpoint);
+
+ aor = strsep(&id, ";@");
ao2_callback(unsolicited_mwi, OBJ_NODATA, send_contact_notify, aor);
}
+/*! \brief Function called when a contact is updated */
+static void mwi_contact_updated(const void *object)
+{
+ mwi_contact_changed(object);
+}
+
/*! \brief Function called when a contact is added */
static void mwi_contact_added(const void *object)
{
+ mwi_contact_changed(object);
+}
+
+/*! \brief Function called when a contact is deleted */
+static void mwi_contact_deleted(const void *object)
+{
const struct ast_sip_contact *contact = object;
struct ao2_iterator *mwi_subs;
struct mwi_subscription *mwi_sub;
- const char *endpoint_id = ast_sorcery_object_get_id(contact->endpoint);
+ struct ast_sip_endpoint *endpoint = NULL;
+ struct ast_sip_contact *found_contact;
- if (ast_strlen_zero(contact->endpoint->subscription.mwi.mailboxes)) {
+ if (contact->endpoint) {
+ endpoint = ao2_bump(contact->endpoint);
+ } else {
+ if (!ast_strlen_zero(contact->endpoint_name)) {
+ endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", contact->endpoint_name);
+ }
+ }
+
+ if (!endpoint || ast_strlen_zero(endpoint->subscription.mwi.mailboxes)) {
+ ao2_cleanup(endpoint);
return;
}
- ao2_lock(unsolicited_mwi);
+ /* Check if there is another contact */
+ found_contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors);
+ ao2_cleanup(endpoint);
+ if (found_contact) {
+ ao2_cleanup(found_contact);
+ return;
+ }
- mwi_subs = ao2_find(unsolicited_mwi, endpoint_id,
+ ao2_lock(unsolicited_mwi);
+ mwi_subs = ao2_find(unsolicited_mwi, contact->endpoint_name,
OBJ_SEARCH_KEY | OBJ_MULTIPLE | OBJ_NOLOCK | OBJ_UNLINK);
if (mwi_subs) {
for (; (mwi_sub = ao2_iterator_next(mwi_subs)); ao2_cleanup(mwi_sub)) {
@@ -1221,18 +1278,14 @@ static void mwi_contact_added(const void *object)
}
ao2_iterator_destroy(mwi_subs);
}
-
- create_mwi_subscriptions_for_endpoint(contact->endpoint, NULL, 0);
-
ao2_unlock(unsolicited_mwi);
-
- mwi_contact_updated(object);
}
/*! \brief Observer for contacts so unsolicited MWI is sent when a contact changes */
static const struct ast_sorcery_observer mwi_contact_observer = {
.created = mwi_contact_added,
.updated = mwi_contact_updated,
+ .deleted = mwi_contact_deleted,
};
/*! \brief Task invoked to send initial MWI NOTIFY for unsolicited */
@@ -1278,7 +1331,9 @@ static struct ast_sorcery_observer global_observer = {
static int reload(void)
{
- create_mwi_subscriptions();
+ if (!ast_sip_get_mwi_disable_initial_unsolicited()) {
+ create_mwi_subscriptions();
+ }
return 0;
}
@@ -1301,13 +1356,13 @@ static int load_module(void)
ast_sip_unregister_subscription_handler(&mwi_handler);
return AST_MODULE_LOAD_DECLINE;
}
- create_mwi_subscriptions();
ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &mwi_contact_observer);
ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &global_observer);
ast_sorcery_reload_object(ast_sip_get_sorcery(), "global");
if (!ast_sip_get_mwi_disable_initial_unsolicited()) {
+ create_mwi_subscriptions();
if (ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
ast_sip_push_task(NULL, send_initial_notify_all, NULL);
} else {
diff --git a/res/res_pjsip_nat.c b/res/res_pjsip_nat.c
index a0ce2a9d1..370004a3a 100644
--- a/res/res_pjsip_nat.c
+++ b/res/res_pjsip_nat.c
@@ -35,6 +35,7 @@
static void rewrite_uri(pjsip_rx_data *rdata, pjsip_sip_uri *uri)
{
pj_cstr(&uri->host, rdata->pkt_info.src_name);
+ uri->port = rdata->pkt_info.src_port;
if (!strcasecmp("WSS", rdata->tp_info.transport->type_name)) {
/* WSS is special, we don't want to overwrite the URI at all as it needs to be ws */
} else if (strcasecmp("udp", rdata->tp_info.transport->type_name)) {
@@ -42,7 +43,6 @@ static void rewrite_uri(pjsip_rx_data *rdata, pjsip_sip_uri *uri)
} else {
uri->transport_param.slen = 0;
}
- uri->port = rdata->pkt_info.src_port;
}
static int rewrite_route_set(pjsip_rx_data *rdata, pjsip_dialog *dlg)
@@ -165,7 +165,7 @@ static int find_transport_state_in_use(void *obj, void *arg, int flags)
((details->type == transport_state->type) && (transport_state->factory) &&
!pj_strcmp(&transport_state->factory->addr_name.host, &details->local_address) &&
transport_state->factory->addr_name.port == details->local_port))) {
- return CMP_MATCH | CMP_STOP;
+ return CMP_MATCH;
}
return 0;
@@ -267,16 +267,16 @@ static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata)
ast_sockaddr_set_port(&addr, tdata->tp_info.dst_port);
/* See if where we are sending this request is local or not, and if not that we can get a Contact URI to modify */
- if (ast_apply_ha(transport_state->localnet, &addr) != AST_SENSE_ALLOW) {
+ if (ast_sip_transport_is_local(transport_state, &addr)) {
ast_debug(5, "Request is being sent to local address, skipping NAT manipulation\n");
return PJ_SUCCESS;
}
}
- if (!ast_sockaddr_isnull(&transport_state->external_address)) {
+ if (!ast_sockaddr_isnull(&transport_state->external_signaling_address)) {
/* Update the contact header with the external address */
if (uri || (uri = nat_get_contact_sip_uri(tdata))) {
- pj_strdup2(tdata->pool, &uri->host, ast_sockaddr_stringify_host(&transport_state->external_address));
+ pj_strdup2(tdata->pool, &uri->host, ast_sockaddr_stringify_host(&transport_state->external_signaling_address));
if (transport->external_signaling_port) {
uri->port = transport->external_signaling_port;
ast_debug(4, "Re-wrote Contact URI port to %d\n", uri->port);
@@ -285,7 +285,7 @@ static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata)
/* Update the via header if relevant */
if ((tdata->msg->type == PJSIP_REQUEST_MSG) && (via || (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL)))) {
- pj_strdup2(tdata->pool, &via->sent_by.host, ast_sockaddr_stringify_host(&transport_state->external_address));
+ pj_strdup2(tdata->pool, &via->sent_by.host, ast_sockaddr_stringify_host(&transport_state->external_signaling_address));
if (transport->external_signaling_port) {
via->sent_by.port = transport->external_signaling_port;
}
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 0a65e6e1d..76d756d9e 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -358,6 +358,8 @@ struct sip_outbound_registration_client_state {
unsigned int auth_attempted:1;
/*! \brief The name of the transport to be used for the registration */
char *transport_name;
+ /*! \brief The name of the registration sorcery object */
+ char *registration_name;
};
/*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
@@ -459,7 +461,7 @@ static int line_identify_relationship(void *obj, void *arg, int flags)
struct sip_outbound_registration_state *state = obj;
pjsip_param *line = arg;
- return !pj_strcmp2(&line->value, state->client_state->line) ? CMP_MATCH | CMP_STOP : 0;
+ return !pj_strcmp2(&line->value, state->client_state->line) ? CMP_MATCH : 0;
}
static struct pjsip_param *get_uri_option_line(const void *uri)
@@ -559,20 +561,21 @@ static int handle_client_registration(void *data)
{
RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
pjsip_tx_data *tdata;
- pjsip_regc_info info;
- char server_uri[PJSIP_MAX_URL_SIZE];
- char client_uri[PJSIP_MAX_URL_SIZE];
if (client_state->status == SIP_REGISTRATION_STOPPED
|| pjsip_regc_register(client_state->client, PJ_FALSE, &tdata) != PJ_SUCCESS) {
return 0;
}
- pjsip_regc_get_info(client_state->client, &info);
- ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
- ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
- ast_debug(1, "Outbound REGISTER attempt %u to '%s' with client '%s'\n",
- client_state->retries + 1, server_uri, client_uri);
+ if (DEBUG_ATLEAST(1)) {
+ pjsip_regc_info info;
+
+ pjsip_regc_get_info(client_state->client, &info);
+ ast_log(LOG_DEBUG, "Outbound REGISTER attempt %u to '%.*s' with client '%.*s'\n",
+ client_state->retries + 1,
+ (int) info.server_uri.slen, info.server_uri.ptr,
+ (int) info.client_uri.slen, info.client_uri.ptr);
+ }
if (client_state->support_path) {
pjsip_supported_hdr *hdr;
@@ -795,6 +798,82 @@ static void schedule_retry(struct registration_response *response, unsigned int
}
}
+static int reregister_immediately_cb(void *obj)
+{
+ struct sip_outbound_registration_state *state = obj;
+
+ if (state->client_state->status != SIP_REGISTRATION_REGISTERED) {
+ ao2_ref(state, -1);
+ return 0;
+ }
+
+ if (DEBUG_ATLEAST(1)) {
+ pjsip_regc_info info;
+
+ pjsip_regc_get_info(state->client_state->client, &info);
+ ast_log(LOG_DEBUG,
+ "Outbound registration transport to server '%.*s' from client '%.*s' shutdown\n",
+ (int) info.server_uri.slen, info.server_uri.ptr,
+ (int) info.client_uri.slen, info.client_uri.ptr);
+ }
+
+ cancel_registration(state->client_state);
+
+ ao2_ref(state->client_state, +1);
+ handle_client_registration(state->client_state);
+
+ ao2_ref(state, -1);
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief The reliable transport we registered using has shutdown.
+ * \since 13.18.0
+ *
+ * \param obj What is needed to initiate a reregister attempt.
+ *
+ * \return Nothing
+ */
+static void registration_transport_shutdown_cb(void *obj)
+{
+ const char *registration_name = obj;
+ struct sip_outbound_registration_state *state;
+
+ state = get_state(registration_name);
+ if (!state) {
+ /* Registration no longer exists or shutting down. */
+ return;
+ }
+ if (ast_sip_push_task(state->client_state->serializer, reregister_immediately_cb, state)) {
+ ao2_ref(state, -1);
+ }
+}
+
+static void registration_transport_monitor_setup(pjsip_transport *transport, const char *registration_name)
+{
+ char *monitor;
+
+ if (!PJSIP_TRANSPORT_IS_RELIABLE(transport)) {
+ return;
+ }
+ monitor = ao2_alloc_options(strlen(registration_name) + 1, NULL,
+ AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!monitor) {
+ return;
+ }
+ strcpy(monitor, registration_name);/* Safe */
+
+ /*
+ * We'll ignore if the transport has already been shutdown before we
+ * register the monitor. We might get into a message spamming infinite
+ * loop of registration, shutdown, reregistration...
+ */
+ ast_sip_transport_monitor_register(transport, registration_transport_shutdown_cb,
+ monitor);
+ ao2_ref(monitor, -1);
+}
+
/*! \brief Callback function for handling a response to a registration attempt */
static int handle_registration_response(void *data)
{
@@ -863,9 +942,15 @@ static int handle_registration_response(void *data)
next_registration_round = 0;
}
schedule_registration(response->client_state, next_registration_round);
+
+ /* See if we should monitor for transport shutdown */
+ registration_transport_monitor_setup(response->rdata->tp_info.transport,
+ response->client_state->registration_name);
} else {
ast_debug(1, "Outbound unregistration to '%s' with client '%s' successful\n", server_uri, client_uri);
update_client_state_status(response->client_state, SIP_REGISTRATION_UNREGISTERED);
+ ast_sip_transport_monitor_unregister(response->rdata->tp_info.transport,
+ registration_transport_shutdown_cb);
}
} else if (response->client_state->destroy) {
/* We need to deal with the pending destruction instead. */
@@ -988,7 +1073,8 @@ static void sip_outbound_registration_state_destroy(void *obj)
struct sip_outbound_registration_state *state = obj;
ast_debug(3, "Destroying registration state for registration to server '%s' from client '%s'\n",
- state->registration->server_uri, state->registration->client_uri);
+ state->registration ? state->registration->server_uri : "",
+ state->registration ? state->registration->client_uri : "");
ao2_cleanup(state->registration);
if (!state->client_state) {
@@ -1007,12 +1093,13 @@ static void sip_outbound_registration_client_state_destroy(void *obj)
{
struct sip_outbound_registration_client_state *client_state = obj;
- ast_free(client_state->transport_name);
ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "-1", 1.0);
ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "-1", 1.0,
sip_outbound_registration_status_str(client_state->status));
ast_taskprocessor_unreference(client_state->serializer);
+ ast_free(client_state->transport_name);
+ ast_free(client_state->registration_name);
}
/*! \brief Allocator function for registration state */
@@ -1032,6 +1119,23 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a
return NULL;
}
+ state->client_state->status = SIP_REGISTRATION_UNREGISTERED;
+ state->client_state->timer.user_data = state->client_state;
+ state->client_state->timer.cb = sip_outbound_registration_timer_cb;
+ state->client_state->transport_name = ast_strdup(registration->transport);
+ state->client_state->registration_name =
+ ast_strdup(ast_sorcery_object_get_id(registration));
+
+ ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "+1", 1.0);
+ ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0,
+ sip_outbound_registration_status_str(state->client_state->status));
+
+ if (!state->client_state->transport_name
+ || !state->client_state->registration_name) {
+ ao2_cleanup(state);
+ return NULL;
+ }
+
/* Create name with seq number appended. */
ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/outreg/%s",
ast_sorcery_object_get_id(registration));
@@ -1042,14 +1146,6 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a
ao2_cleanup(state);
return NULL;
}
- state->client_state->status = SIP_REGISTRATION_UNREGISTERED;
- state->client_state->timer.user_data = state->client_state;
- state->client_state->timer.cb = sip_outbound_registration_timer_cb;
- state->client_state->transport_name = ast_strdup(registration->transport);
-
- ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "+1", 1.0);
- ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0,
- sip_outbound_registration_status_str(state->client_state->status));
state->registration = ao2_bump(registration);
return state;
@@ -2053,6 +2149,8 @@ static int unload_module(void)
ao2_global_obj_release(current_states);
+ ast_sip_transport_monitor_unregister_all(registration_transport_shutdown_cb);
+
/* Wait for registration serializers to get destroyed. */
ast_debug(2, "Waiting for registration transactions to complete for unload.\n");
remaining = ast_serializer_shutdown_group_join(shutdown_group, MAX_UNLOAD_TIMEOUT_TIME);
diff --git a/res/res_pjsip_pidf_body_generator.c b/res/res_pjsip_pidf_body_generator.c
index d3be8c131..4daff964d 100644
--- a/res/res_pjsip_pidf_body_generator.c
+++ b/res/res_pjsip_pidf_body_generator.c
@@ -58,7 +58,7 @@ static int pidf_generate_body_content(void *body, void *data)
struct ast_sip_exten_state_data *state_data = data;
ast_sip_presence_exten_state_to_str(state_data->exten_state, &statestring,
- &pidfstate, &pidfnote, &local_state);
+ &pidfstate, &pidfnote, &local_state, 0);
if (!pjpidf_pres_add_note(state_data->pool, pres, pj_cstr(&note, pidfnote))) {
ast_log(LOG_WARNING, "Unable to add note to PIDF presence\n");
@@ -75,7 +75,7 @@ static int pidf_generate_body_content(void *body, void *data)
pjpidf_tuple_set_contact(state_data->pool, tuple, pj_cstr(&contact, sanitized));
pjpidf_tuple_set_contact_prio(state_data->pool, tuple, pj_cstr(&priority, "1"));
pjpidf_status_set_basic_open(pjpidf_tuple_get_status(tuple),
- local_state == NOTIFY_OPEN);
+ local_state == NOTIFY_OPEN || local_state == NOTIFY_INUSE);
return 0;
}
diff --git a/res/res_pjsip_pidf_eyebeam_body_supplement.c b/res/res_pjsip_pidf_eyebeam_body_supplement.c
index cd590c3d3..0200a4654 100644
--- a/res/res_pjsip_pidf_eyebeam_body_supplement.c
+++ b/res/res_pjsip_pidf_eyebeam_body_supplement.c
@@ -46,30 +46,28 @@
*/
static void add_eyebeam(pj_pool_t *pool, pj_xml_node *node, const char *pidfstate)
{
- static const char *XMLNS_PP = "xmlns:pp";
- static const char *XMLNS_PERSON = "urn:ietf:params:xml:ns:pidf:person";
+ static const char *XMLNS_DM_PREFIX = "xmlns:dm";
+ static const char *XMLNS_DM = "urn:ietf:params:xml:ns:pidf:data-model";
- static const char *XMLNS_ES = "xmlns:es";
- static const char *XMLNS_RPID_STATUS = "urn:ietf:params:xml:ns:pidf:rpid:status:rpid-status";
+ static const char *XMLNS_RPID_PREFIX = "xmlns:rpid";
+ static const char *XMLNS_RPID = "urn:ietf:params:xml:ns:pidf:rpid";
- static const char *XMLNS_EP = "xmlns:ep";
- static const char *XMLNS_RPID_PERSON = "urn:ietf:params:xml:ns:pidf:rpid:rpid-person";
-
- pj_xml_node *person = ast_sip_presence_xml_create_node(pool, node, "pp:person");
- pj_xml_node *status = ast_sip_presence_xml_create_node(pool, person, "status");
+ pj_xml_node *person = ast_sip_presence_xml_create_node(pool, node, "dm:person");
if (pidfstate[0] != '-') {
- pj_xml_node *activities = ast_sip_presence_xml_create_node(pool, status, "ep:activities");
- size_t str_size = sizeof("ep:") + strlen(pidfstate);
+ pj_xml_node *activities = ast_sip_presence_xml_create_node(pool, person, "rpid:activities");
+ size_t str_size = sizeof("rpid:") + strlen(pidfstate);
+ char *act_str = ast_alloca(str_size);
+
+ /* Safe */
+ strcpy(act_str, "rpid:");
+ strcat(act_str, pidfstate);
- activities->content.ptr = pj_pool_alloc(pool, str_size);
- activities->content.slen = pj_ansi_snprintf(activities->content.ptr, str_size,
- "ep:%s", pidfstate);
+ ast_sip_presence_xml_create_node(pool, activities, act_str);
}
- ast_sip_presence_xml_create_attr(pool, node, XMLNS_PP, XMLNS_PERSON);
- ast_sip_presence_xml_create_attr(pool, node, XMLNS_ES, XMLNS_RPID_STATUS);
- ast_sip_presence_xml_create_attr(pool, node, XMLNS_EP, XMLNS_RPID_PERSON);
+ ast_sip_presence_xml_create_attr(pool, node, XMLNS_DM_PREFIX, XMLNS_DM);
+ ast_sip_presence_xml_create_attr(pool, node, XMLNS_RPID_PREFIX, XMLNS_RPID);
}
static int pidf_supplement_body(void *body, void *data)
@@ -80,7 +78,7 @@ static int pidf_supplement_body(void *body, void *data)
enum ast_sip_pidf_state local_state;
ast_sip_presence_exten_state_to_str(state_data->exten_state, &statestring,
- &pidfstate, &pidfnote, &local_state);
+ &pidfstate, &pidfnote, &local_state, 0);
add_eyebeam(state_data->pool, pres, pidfstate);
return 0;
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index fbb1ad4e8..c62bddd56 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -1108,7 +1108,9 @@ static void remove_subscription(struct sip_subscription_tree *obj)
static void destroy_subscription(struct ast_sip_subscription *sub)
{
ast_debug(3, "Destroying SIP subscription from '%s->%s'\n",
- ast_sorcery_object_get_id(sub->tree->endpoint), sub->resource);
+ sub->tree->endpoint ? ast_sorcery_object_get_id(sub->tree->endpoint) : "Unknown",
+ sub->resource);
+
ast_free(sub->body_text);
AST_VECTOR_FREE(&sub->children);
@@ -1265,14 +1267,14 @@ static void subscription_tree_destructor(void *obj)
sub_tree->endpoint ? ast_sorcery_object_get_id(sub_tree->endpoint) : "Unknown",
sub_tree->root ? sub_tree->root->resource : "Unknown");
- ao2_cleanup(sub_tree->endpoint);
-
destroy_subscriptions(sub_tree->root);
if (sub_tree->dlg) {
ast_sip_push_task_synchronous(sub_tree->serializer, subscription_unreference_dialog, sub_tree);
}
+ ao2_cleanup(sub_tree->endpoint);
+
ast_taskprocessor_unreference(sub_tree->serializer);
ast_module_unref(ast_module_info->self);
}
diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c
index 111c4b2a1..ff8bc6a66 100644
--- a/res/res_pjsip_refer.c
+++ b/res/res_pjsip_refer.c
@@ -543,6 +543,7 @@ static int refer_attended_task(void *data)
}
}
+ ast_sip_session_end_if_deferred(attended->transferer);
if (response != 200) {
if (!ast_sip_push_task(attended->transferer->serializer,
defer_termination_cancel, attended->transferer)) {
@@ -772,6 +773,7 @@ static int refer_incoming_attended_request(struct ast_sip_session *session, pjsi
/* Push it to the other session, which will have both channels with minimal locking */
if (ast_sip_push_task(other_session->serializer, refer_attended_task, attended)) {
+ ast_sip_session_end_if_deferred(session);
ast_sip_session_defer_termination_cancel(session);
ao2_cleanup(attended);
return 500;
@@ -810,9 +812,12 @@ static int refer_incoming_attended_request(struct ast_sip_session *session, pjsi
response = xfer_response_code2sip(ast_bridge_transfer_blind(1, session->channel,
"external_replaces", context, refer_blind_callback, &refer));
+
+ ast_sip_session_end_if_deferred(session);
if (response != 200) {
ast_sip_session_defer_termination_cancel(session);
}
+
return response;
}
}
@@ -865,9 +870,12 @@ static int refer_incoming_blind_request(struct ast_sip_session *session, pjsip_r
response = xfer_response_code2sip(ast_bridge_transfer_blind(1, session->channel,
exten, context, refer_blind_callback, &refer));
+
+ ast_sip_session_end_if_deferred(session);
if (response != 200) {
ast_sip_session_defer_termination_cancel(session);
}
+
return response;
}
diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c
index d54bffa0c..48b48356c 100644
--- a/res/res_pjsip_registrar.c
+++ b/res/res_pjsip_registrar.c
@@ -123,7 +123,7 @@ static int registrar_find_contact(void *obj, void *arg, int flags)
const struct registrar_contact_details *details = arg;
pjsip_uri *contact_uri = pjsip_parse_uri(details->pool, (char*)contact->uri, strlen(contact->uri), 0);
- return (pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, details->uri, contact_uri) == PJ_SUCCESS) ? CMP_MATCH | CMP_STOP : 0;
+ return (pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, details->uri, contact_uri) == PJ_SUCCESS) ? CMP_MATCH : 0;
}
/*! \brief Internal function which validates provided Contact headers to confirm that they are acceptable, and returns number of contacts */
@@ -310,6 +310,47 @@ static int registrar_validate_path(pjsip_rx_data *rdata, struct ast_sip_aor *aor
return -1;
}
+/*! Transport monitor for incoming REGISTER contacts */
+struct contact_transport_monitor {
+ /*!
+ * \brief Sorcery contact name to remove on transport shutdown
+ * \note Stored after aor_name in space reserved when struct allocated.
+ */
+ char *contact_name;
+ /*! AOR name the contact is associated */
+ char aor_name[0];
+};
+
+static void register_contact_transport_shutdown_cb(void *data)
+{
+ struct contact_transport_monitor *monitor = data;
+ struct ast_sip_contact *contact;
+ struct ast_named_lock *lock;
+
+ lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_MUTEX, "aor", monitor->aor_name);
+ if (!lock) {
+ return;
+ }
+
+ ao2_lock(lock);
+ contact = ast_sip_location_retrieve_contact(monitor->contact_name);
+ if (contact) {
+ ast_sip_location_delete_contact(contact);
+ ast_verb(3, "Removed contact '%s' from AOR '%s' due to transport shutdown\n",
+ contact->uri, monitor->aor_name);
+ ast_test_suite_event_notify("AOR_CONTACT_REMOVED",
+ "Contact: %s\r\n"
+ "AOR: %s\r\n"
+ "UserAgent: %s",
+ contact->uri,
+ monitor->aor_name,
+ contact->user_agent);
+ ao2_ref(contact, -1);
+ }
+ ao2_unlock(lock);
+ ast_named_lock_put(lock);
+}
+
static int register_aor_core(pjsip_rx_data *rdata,
struct ast_sip_endpoint *endpoint,
struct ast_sip_aor *aor,
@@ -419,6 +460,9 @@ static int register_aor_core(pjsip_rx_data *rdata,
pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri));
if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) {
+ int prune_on_boot = 0;
+ pj_str_t host_name;
+
/* If they are actually trying to delete a contact that does not exist... be forgiving */
if (!expiration) {
ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n",
@@ -426,14 +470,68 @@ static int register_aor_core(pjsip_rx_data *rdata,
continue;
}
- if (ast_sip_location_add_contact_nolock(aor, contact_uri, ast_tvadd(ast_tvnow(),
- ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL,
- user_agent, via_addr, via_port, call_id, endpoint)) {
+ /* Determine if the contact cannot survive a restart/boot. */
+ if (details.uri->port == rdata->pkt_info.src_port
+ && !pj_strcmp(&details.uri->host,
+ pj_cstr(&host_name, rdata->pkt_info.src_name))
+ /* We have already checked if the URI scheme is sip: or sips: */
+ && PJSIP_TRANSPORT_IS_RELIABLE(rdata->tp_info.transport)) {
+ pj_str_t type_name;
+
+ /* Determine the transport parameter value */
+ if (!strcasecmp("WSS", rdata->tp_info.transport->type_name)) {
+ /* WSS is special, as it needs to be ws. */
+ pj_cstr(&type_name, "ws");
+ } else {
+ pj_cstr(&type_name, rdata->tp_info.transport->type_name);
+ }
+
+ if (!pj_stricmp(&details.uri->transport_param, &type_name)
+ && (endpoint->nat.rewrite_contact
+ /* Websockets are always rewritten */
+ || !pj_stricmp(&details.uri->transport_param,
+ pj_cstr(&type_name, "ws")))) {
+ /*
+ * The contact was rewritten to the reliable transport's
+ * source address. Disconnecting the transport for any
+ * reason invalidates the contact.
+ */
+ prune_on_boot = 1;
+ }
+ }
+
+ contact = ast_sip_location_create_contact(aor, contact_uri,
+ ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)),
+ path_str ? ast_str_buffer(path_str) : NULL,
+ user_agent, via_addr, via_port, call_id, prune_on_boot, endpoint);
+ if (!contact) {
ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n",
- contact_uri, aor_name);
+ contact_uri, aor_name);
continue;
}
+ if (prune_on_boot) {
+ const char *contact_name;
+ struct contact_transport_monitor *monitor;
+
+ /*
+ * Monitor the transport in case it gets disconnected because
+ * the contact won't be valid anymore if that happens.
+ */
+ contact_name = ast_sorcery_object_get_id(contact);
+ monitor = ao2_alloc_options(sizeof(*monitor) + 2 + strlen(aor_name)
+ + strlen(contact_name), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (monitor) {
+ strcpy(monitor->aor_name, aor_name);/* Safe */
+ monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1;
+ strcpy(monitor->contact_name, contact_name);/* Safe */
+
+ ast_sip_transport_monitor_register(rdata->tp_info.transport,
+ register_contact_transport_shutdown_cb, monitor);
+ ao2_ref(monitor, -1);
+ }
+ }
+
ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n",
contact_uri, aor_name, expiration);
ast_test_suite_event_notify("AOR_CONTACT_ADDED",
@@ -893,6 +991,7 @@ static int unload_module(void)
ast_manager_unregister(AMI_SHOW_REGISTRATIONS);
ast_manager_unregister(AMI_SHOW_REGISTRATION_CONTACT_STATUSES);
ast_sip_unregister_service(&registrar_module);
+ ast_sip_transport_monitor_unregister_all(register_contact_transport_shutdown_cb);
return 0;
}
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index cafbd52ec..b8ae8c185 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -241,15 +241,16 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me
}
ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_NAT, session->endpoint->media.rtp.symmetric);
+ ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_ASYMMETRIC_CODEC, session->endpoint->asymmetric_rtp_codec);
if (!session->endpoint->media.rtp.ice_support && (ice = ast_rtp_instance_get_ice(session_media->rtp))) {
ice->stop(session_media->rtp);
}
- if (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733 || session->endpoint->dtmf == AST_SIP_DTMF_AUTO) {
+ if (session->dtmf == AST_SIP_DTMF_RFC_4733 || session->dtmf == AST_SIP_DTMF_AUTO || session->dtmf == AST_SIP_DTMF_AUTO_INFO) {
ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_RFC2833);
ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_DTMF, 1);
- } else if (session->endpoint->dtmf == AST_SIP_DTMF_INBAND) {
+ } else if (session->dtmf == AST_SIP_DTMF_INBAND) {
ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND);
}
@@ -269,7 +270,7 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me
}
static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp_media *stream, struct ast_rtp_codecs *codecs,
- struct ast_sip_session_media *session_media)
+ struct ast_sip_session_media *session_media)
{
pjmedia_sdp_attr *attr;
pjmedia_sdp_rtpmap *rtpmap;
@@ -332,9 +333,19 @@ static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp
}
}
}
- if (!tel_event && (session->endpoint->dtmf == AST_SIP_DTMF_AUTO)) {
+ if (!tel_event && (session->dtmf == AST_SIP_DTMF_AUTO)) {
ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND);
}
+
+ if (session->dtmf == AST_SIP_DTMF_AUTO_INFO) {
+ if (tel_event) {
+ ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_RFC2833);
+ } else {
+ ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_NONE);
+ }
+ }
+
+
/* Get the packetization, if it exists */
if ((attr = pjmedia_sdp_media_find_attr2(stream, "ptime", NULL))) {
unsigned long framing = pj_strtoul(pj_strltrim(&attr->value));
@@ -401,7 +412,24 @@ static int set_caps(struct ast_sip_session *session, struct ast_sip_session_medi
ast_format_cap_append_from_cap(caps, ast_channel_nativeformats(session->channel),
AST_MEDIA_TYPE_UNKNOWN);
ast_format_cap_remove_by_type(caps, media_type);
- ast_format_cap_append_from_cap(caps, joint, media_type);
+
+ /*
+ * If we don't allow the sending codec to be changed on our side
+ * then get the best codec from the joint capabilities of the media
+ * type and use only that. This ensures the core won't start sending
+ * out a format that we aren't currently sending.
+ */
+ if (!session->endpoint->asymmetric_rtp_codec) {
+ struct ast_format *best;
+
+ best = ast_format_cap_get_best_by_type(joint, media_type);
+ if (best) {
+ ast_format_cap_append(caps, best, ast_format_cap_get_framing(joint));
+ ao2_ref(best, -1);
+ }
+ } else {
+ ast_format_cap_append_from_cap(caps, joint, media_type);
+ }
/*
* Apply the new formats to the channel, potentially changing
@@ -412,7 +440,8 @@ static int set_caps(struct ast_sip_session *session, struct ast_sip_session_medi
ast_set_read_format(session->channel, ast_channel_readformat(session->channel));
ast_set_write_format(session->channel, ast_channel_writeformat(session->channel));
}
- if ((session->endpoint->dtmf == AST_SIP_DTMF_AUTO)
+
+ if ( ((session->dtmf == AST_SIP_DTMF_AUTO) || (session->dtmf == AST_SIP_DTMF_AUTO_INFO) )
&& (ast_rtp_instance_dtmf_mode_get(session_media->rtp) == AST_RTP_DTMF_MODE_RFC2833)
&& (session->dsp)) {
dsp_features = ast_dsp_get_features(session->dsp);
@@ -1132,7 +1161,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
pj_str_t stmp;
pjmedia_sdp_attr *attr;
int index = 0;
- int noncodec = (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733 || session->endpoint->dtmf == AST_SIP_DTMF_AUTO) ? AST_RTP_DTMF : 0;
+ int noncodec = (session->dtmf == AST_SIP_DTMF_RFC_4733 || session->dtmf == AST_SIP_DTMF_AUTO || session->dtmf == AST_SIP_DTMF_AUTO_INFO) ? AST_RTP_DTMF : 0;
int min_packet_size = 0, max_packet_size = 0;
int rtp_code;
RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup);
@@ -1488,12 +1517,11 @@ static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struc
ast_sockaddr_parse(&addr, host, PARSE_PORT_FORBID);
/* Is the address within the SDP inside the same network? */
- if (transport_state->localnet
- && ast_apply_ha(transport_state->localnet, &addr) == AST_SENSE_ALLOW) {
+ if (ast_sip_transport_is_local(transport_state, &addr)) {
return;
}
- ast_debug(5, "Setting media address to %s\n", transport->external_media_address);
- pj_strdup2(tdata->pool, &stream->conn->addr, transport->external_media_address);
+ ast_debug(5, "Setting media address to %s\n", ast_sockaddr_stringify_host(&transport_state->external_media_address));
+ pj_strdup2(tdata->pool, &stream->conn->addr, ast_sockaddr_stringify_host(&transport_state->external_media_address));
}
/*! \brief Function which stops the RTP instance */
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 1fec089f8..ab6fce2c8 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -968,6 +968,46 @@ int ast_sip_session_refresh(struct ast_sip_session *session,
return 0;
}
+int ast_sip_session_regenerate_answer(struct ast_sip_session *session,
+ ast_sip_session_sdp_creation_cb on_sdp_creation)
+{
+ pjsip_inv_session *inv_session = session->inv_session;
+ pjmedia_sdp_session *new_answer = NULL;
+ const pjmedia_sdp_session *previous_offer = NULL;
+
+ /* The SDP answer can only be regenerated if it is still pending to be sent */
+ if (!inv_session->neg || (pjmedia_sdp_neg_get_state(inv_session->neg) != PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER &&
+ pjmedia_sdp_neg_get_state(inv_session->neg) != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO)) {
+ ast_log(LOG_WARNING, "Requested to regenerate local SDP answer for channel '%s' but negotiation in state '%s'\n",
+ ast_channel_name(session->channel), pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_get_state(inv_session->neg)));
+ return -1;
+ }
+
+ pjmedia_sdp_neg_get_neg_remote(inv_session->neg, &previous_offer);
+ if (pjmedia_sdp_neg_get_state(inv_session->neg) == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) {
+ /* Transition the SDP negotiator back to when it received the remote offer */
+ pjmedia_sdp_neg_negotiate(inv_session->pool, inv_session->neg, 0);
+ pjmedia_sdp_neg_set_remote_offer(inv_session->pool, inv_session->neg, previous_offer);
+ }
+
+ new_answer = create_local_sdp(inv_session, session, previous_offer);
+ if (!new_answer) {
+ ast_log(LOG_WARNING, "Could not create a new local SDP answer for channel '%s'\n",
+ ast_channel_name(session->channel));
+ return -1;
+ }
+
+ if (on_sdp_creation) {
+ if (on_sdp_creation(session, new_answer)) {
+ return -1;
+ }
+ }
+
+ pjsip_inv_set_sdp_answer(inv_session, new_answer);
+
+ return 0;
+}
+
void ast_sip_session_send_response(struct ast_sip_session *session, pjsip_tx_data *tdata)
{
handle_outgoing_response(session, tdata);
@@ -1481,6 +1521,8 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
session->contact = ao2_bump(contact);
session->inv_session = inv_session;
+ session->dtmf = endpoint->dtmf;
+
if (add_supplements(session)) {
/* Release the ref held by session->inv_session */
ao2_ref(session, -1);
@@ -1903,6 +1945,9 @@ int ast_sip_session_defer_termination(struct ast_sip_session *session)
session->defer_terminate = 1;
+ session->defer_end = 1;
+ session->ended_while_deferred = 0;
+
session->scheduled_termination.id = 0;
ao2_ref(session, +1);
session->scheduled_termination.user_data = session;
@@ -1940,6 +1985,7 @@ void ast_sip_session_defer_termination_cancel(struct ast_sip_session *session)
/* Already canceled or timer fired. */
return;
}
+
session->defer_terminate = 0;
if (session->terminate_while_deferred) {
@@ -1951,6 +1997,22 @@ void ast_sip_session_defer_termination_cancel(struct ast_sip_session *session)
sip_session_defer_termination_stop_timer(session);
}
+void ast_sip_session_end_if_deferred(struct ast_sip_session *session)
+{
+ if (!session->defer_end) {
+ return;
+ }
+
+ session->defer_end = 0;
+
+ if (session->ended_while_deferred) {
+ /* Complete the session end started by the remote hangup. */
+ ast_debug(3, "Ending session (%p) after being deferred\n", session);
+ session->ended_while_deferred = 0;
+ session_end(session);
+ }
+}
+
struct ast_sip_session *ast_sip_dialog_get_session(pjsip_dialog *dlg)
{
pjsip_inv_session *inv_session = pjsip_dlg_get_inv_session(dlg);
@@ -2532,6 +2594,11 @@ static int session_end(void *vsession)
iter->session_end(session);
}
}
+
+ /* Release any media resources. */
+ ao2_cleanup(session->media);
+ session->media = NULL;
+
return 0;
}
@@ -2636,6 +2703,12 @@ static void session_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
}
if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
+ if (session->defer_end) {
+ ast_debug(3, "Deferring session (%p) end\n", session);
+ session->ended_while_deferred = 1;
+ return;
+ }
+
if (ast_sip_push_task(session->serializer, session_end, session)) {
/* Do it anyway even though this is not the right thread. */
session_end(session);
@@ -3122,11 +3195,9 @@ static void session_outgoing_nat_hook(pjsip_tx_data *tdata, struct ast_sip_trans
ast_copy_pj_str(host, &sdp->conn->addr, sizeof(host));
ast_sockaddr_parse(&addr, host, PARSE_PORT_FORBID);
- if (!transport_state->localnet
- || (transport_state->localnet
- && ast_apply_ha(transport_state->localnet, &addr) == AST_SENSE_ALLOW)) {
- ast_debug(5, "Setting external media address to %s\n", transport->external_media_address);
- pj_strdup2(tdata->pool, &sdp->conn->addr, transport->external_media_address);
+ if (ast_sip_transport_is_nonlocal(transport_state, &addr)) {
+ ast_debug(5, "Setting external media address to %s\n", ast_sockaddr_stringify_host(&transport_state->external_media_address));
+ pj_strdup2(tdata->pool, &sdp->conn->addr, ast_sockaddr_stringify_host(&transport_state->external_media_address));
}
}
diff --git a/res/res_pjsip_session.exports.in b/res/res_pjsip_session.exports.in
index a39485e66..5bc0bf40e 100644
--- a/res/res_pjsip_session.exports.in
+++ b/res/res_pjsip_session.exports.in
@@ -3,6 +3,7 @@
LINKER_SYMBOL_PREFIXast_sip_session_terminate;
LINKER_SYMBOL_PREFIXast_sip_session_defer_termination;
LINKER_SYMBOL_PREFIXast_sip_session_defer_termination_cancel;
+ LINKER_SYMBOL_PREFIXast_sip_session_end_if_deferred;
LINKER_SYMBOL_PREFIXast_sip_session_register_sdp_handler;
LINKER_SYMBOL_PREFIXast_sip_session_unregister_sdp_handler;
LINKER_SYMBOL_PREFIXast_sip_session_register_supplement;
@@ -13,6 +14,7 @@
LINKER_SYMBOL_PREFIXast_sip_session_remove_datastore;
LINKER_SYMBOL_PREFIXast_sip_session_get_identity;
LINKER_SYMBOL_PREFIXast_sip_session_refresh;
+ LINKER_SYMBOL_PREFIXast_sip_session_regenerate_answer;
LINKER_SYMBOL_PREFIXast_sip_session_send_response;
LINKER_SYMBOL_PREFIXast_sip_session_send_request;
LINKER_SYMBOL_PREFIXast_sip_session_create_invite;
diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c
index 0ce6474e3..58da6a080 100644
--- a/res/res_pjsip_t38.c
+++ b/res/res_pjsip_t38.c
@@ -278,7 +278,7 @@ static int t38_reinvite_sdp_cb(struct ast_sip_session *session, pjmedia_sdp_sess
/* Move the image media stream to the front and have it as the only stream, pjmedia will fill in
* dummy streams for the rest
*/
- for (stream = 0; stream < sdp->media_count++; ++stream) {
+ for (stream = 0; stream < sdp->media_count; ++stream) {
if (!pj_strcmp2(&sdp->media[stream]->desc.media, "image")) {
sdp->media[0] = sdp->media[stream];
sdp->media_count = 1;
@@ -294,21 +294,22 @@ static int t38_reinvite_response_cb(struct ast_sip_session *session, pjsip_rx_da
{
struct pjsip_status_line status = rdata->msg_info.msg->line.status;
struct t38_state *state;
- RAII_VAR(struct ast_sip_session_media *, session_media, NULL, ao2_cleanup);
+ struct ast_sip_session_media *session_media = NULL;
if (status.code == 100) {
return 0;
}
- if (!(state = t38_state_get_or_alloc(session)) ||
+ if (!session->channel || !(state = t38_state_get_or_alloc(session)) ||
!(session_media = ao2_find(session->media, "image", OBJ_KEY))) {
ast_log(LOG_WARNING, "Received response to T.38 re-invite on '%s' but state unavailable\n",
- ast_channel_name(session->channel));
+ session->channel ? ast_channel_name(session->channel) : "unknown channel");
return 0;
}
t38_change_state(session, session_media, state, (status.code == 200) ? T38_ENABLED : T38_REJECTED);
+ ao2_cleanup(session_media);
return 0;
}
@@ -403,16 +404,21 @@ static int t38_interpret_parameters(void *obj)
static struct ast_frame *t38_framehook_write(struct ast_channel *chan,
struct ast_sip_session *session, struct ast_frame *f)
{
- if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_T38_PARAMETERS &&
- session->endpoint->media.t38.enabled) {
- struct t38_parameters_task_data *data = t38_parameters_task_data_alloc(session, f);
+ if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_T38_PARAMETERS) {
+ if (session->endpoint->media.t38.enabled) {
+ struct t38_parameters_task_data *data = t38_parameters_task_data_alloc(session, f);
- if (!data) {
- return f;
- }
+ if (!data) {
+ return f;
+ }
- if (ast_sip_push_task(session->serializer, t38_interpret_parameters, data)) {
- ao2_ref(data, -1);
+ if (ast_sip_push_task(session->serializer, t38_interpret_parameters, data)) {
+ ao2_ref(data, -1);
+ }
+ } else {
+ struct ast_control_t38_parameters parameters = { .request_response = AST_T38_REFUSED, };
+ ast_debug(2, "T.38 support not enabled, rejecting T.38 control packet\n");
+ ast_queue_control_data(session->channel, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
}
} else if (f->frametype == AST_FRAME_MODEM) {
struct ast_sip_session_media *session_media;
@@ -503,10 +509,7 @@ static void t38_attach_framehook(struct ast_sip_session *session)
return;
}
- /* Only attach the framehook if t38 is enabled for the endpoint */
- if (!session->endpoint->media.t38.enabled) {
- return;
- }
+ /* Always attach the framehook so we can quickly reject */
ast_channel_lock(session->channel);
@@ -879,12 +882,11 @@ static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struc
ast_sockaddr_parse(&addr, host, PARSE_PORT_FORBID);
/* Is the address within the SDP inside the same network? */
- if (transport_state->localnet
- && ast_apply_ha(transport_state->localnet, &addr) == AST_SENSE_ALLOW) {
+ if (ast_sip_transport_is_local(transport_state, &addr)) {
return;
}
- ast_debug(5, "Setting media address to %s\n", transport->external_media_address);
- pj_strdup2(tdata->pool, &stream->conn->addr, transport->external_media_address);
+ ast_debug(5, "Setting media address to %s\n", ast_sockaddr_stringify_host(&transport_state->external_media_address));
+ pj_strdup2(tdata->pool, &stream->conn->addr, ast_sockaddr_stringify_host(&transport_state->external_media_address));
}
/*! \brief Function which destroys the UDPTL instance when session ends */
diff --git a/res/res_pjsip_transport_management.c b/res/res_pjsip_transport_management.c
index 3e129dc5f..eb92eb7a5 100644
--- a/res/res_pjsip_transport_management.c
+++ b/res/res_pjsip_transport_management.c
@@ -33,8 +33,8 @@
#include "asterisk/module.h"
#include "asterisk/astobj2.h"
-/*! \brief Number of buckets for keepalive transports */
-#define TRANSPORTS_BUCKETS 53
+/*! \brief Number of buckets for monitored transports */
+#define TRANSPORTS_BUCKETS 127
#define IDLE_TIMEOUT (pjsip_cfg()->tsx.td)
@@ -53,9 +53,6 @@ static pthread_t keepalive_thread = AST_PTHREADT_NULL;
/*! \brief The global interval at which to send keepalives */
static unsigned int keepalive_interval;
-/*! \brief Existing transport manager callback that we need to invoke */
-static pjsip_tp_state_callback tpmgr_state_callback;
-
/*! \brief Structure for transport to be monitored */
struct monitored_transport {
/*! \brief The underlying PJSIP transport */
@@ -114,7 +111,7 @@ AST_THREADSTORAGE(desc_storage);
static int idle_sched_cb(const void *data)
{
- struct monitored_transport *keepalive = (struct monitored_transport *) data;
+ struct monitored_transport *monitored = (struct monitored_transport *) data;
if (!pj_thread_is_registered()) {
pj_thread_t *thread;
@@ -123,7 +120,7 @@ static int idle_sched_cb(const void *data)
desc = ast_threadstorage_get(&desc_storage, sizeof(pj_thread_desc));
if (!desc) {
ast_log(LOG_ERROR, "Could not get thread desc from thread-local storage.\n");
- ao2_ref(keepalive, -1);
+ ao2_ref(monitored, -1);
return 0;
}
@@ -132,22 +129,22 @@ static int idle_sched_cb(const void *data)
pj_thread_register("Transport Monitor", *desc, &thread);
}
- if (!keepalive->sip_received) {
+ if (!monitored->sip_received) {
ast_log(LOG_NOTICE, "Shutting down transport '%s' since no request was received in %d seconds\n",
- keepalive->transport->info, IDLE_TIMEOUT / 1000);
- pjsip_transport_shutdown(keepalive->transport);
+ monitored->transport->info, IDLE_TIMEOUT / 1000);
+ pjsip_transport_shutdown(monitored->transport);
}
- ao2_ref(keepalive, -1);
+ ao2_ref(monitored, -1);
return 0;
}
/*! \brief Destructor for keepalive transport */
static void monitored_transport_destroy(void *obj)
{
- struct monitored_transport *keepalive = obj;
+ struct monitored_transport *monitored = obj;
- pjsip_transport_dec_ref(keepalive->transport);
+ pjsip_transport_dec_ref(monitored->transport);
}
/*! \brief Callback invoked when transport changes occur */
@@ -178,14 +175,13 @@ static void monitored_transport_state_callback(pjsip_transport *transport, pjsip
/* Let the scheduler inherit the reference from allocation */
if (ast_sched_add_variable(sched, IDLE_TIMEOUT, idle_sched_cb, monitored, 1) < 0) {
/* Uh Oh. Could not schedule the idle check. Kill the transport. */
- ao2_unlink(transports, monitored);
- ao2_ref(monitored, -1);
pjsip_transport_shutdown(transport);
+ } else {
+ /* monitored ref successfully passed to idle_sched_cb() */
+ break;
}
- } else {
- /* No scheduled task, so get rid of the allocation reference */
- ao2_ref(monitored, -1);
}
+ ao2_ref(monitored, -1);
break;
case PJSIP_TP_STATE_SHUTDOWN:
case PJSIP_TP_STATE_DISCONNECTED:
@@ -197,13 +193,12 @@ static void monitored_transport_state_callback(pjsip_transport *transport, pjsip
ao2_ref(transports, -1);
}
-
- /* Forward to the old state callback if present */
- if (tpmgr_state_callback) {
- tpmgr_state_callback(transport, state, info);
- }
}
+struct ast_sip_tpmgr_state_callback monitored_transport_reg = {
+ monitored_transport_state_callback,
+};
+
/*! \brief Hashing function for monitored transport */
static int monitored_transport_hash_fn(const void *obj, int flags)
{
@@ -327,16 +322,9 @@ static pjsip_module idle_monitor_module = {
static int load_module(void)
{
struct ao2_container *transports;
- pjsip_tpmgr *tpmgr;
CHECK_PJSIP_MODULE_LOADED();
- tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
- if (!tpmgr) {
- ast_log(LOG_ERROR, "No transport manager to attach keepalive functionality to.\n");
- return AST_MODULE_LOAD_DECLINE;
- }
-
transports = ao2_container_alloc(TRANSPORTS_BUCKETS, monitored_transport_hash_fn,
monitored_transport_cmp_fn);
if (!transports) {
@@ -363,8 +351,7 @@ static int load_module(void)
ast_sip_register_service(&idle_monitor_module);
- tpmgr_state_callback = pjsip_tpmgr_get_state_cb(tpmgr);
- pjsip_tpmgr_set_state_cb(tpmgr, &monitored_transport_state_callback);
+ ast_sip_transport_state_register(&monitored_transport_reg);
ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &keepalive_global_observer);
ast_sorcery_reload_object(ast_sip_get_sorcery(), "global");
@@ -375,8 +362,6 @@ static int load_module(void)
static int unload_module(void)
{
- pjsip_tpmgr *tpmgr;
-
if (keepalive_interval) {
keepalive_interval = 0;
if (keepalive_thread != AST_PTHREADT_NULL) {
@@ -388,10 +373,7 @@ static int unload_module(void)
ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &keepalive_global_observer);
- tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
- if (tpmgr) {
- pjsip_tpmgr_set_state_cb(tpmgr, tpmgr_state_callback);
- }
+ ast_sip_transport_state_unregister(&monitored_transport_reg);
ast_sip_unregister_service(&idle_monitor_module);
diff --git a/res/res_pjsip_transport_websocket.c b/res/res_pjsip_transport_websocket.c
index 1b9d616de..c04594fee 100644
--- a/res/res_pjsip_transport_websocket.c
+++ b/res/res_pjsip_transport_websocket.c
@@ -145,6 +145,7 @@ static int transport_create(void *data)
{
struct transport_create_data *create_data = data;
struct ws_transport *newtransport = NULL;
+ pjsip_tp_state_callback state_cb;
pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
struct pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(endpt);
@@ -161,6 +162,10 @@ static int transport_create(void *data)
goto on_error;
}
+ /* Give websocket transport a unique name for its lifetime */
+ snprintf(newtransport->transport.obj_name, PJ_MAX_OBJ_NAME, "ws%p",
+ &newtransport->transport);
+
newtransport->transport.endpt = endpt;
if (!(pool = pjsip_endpt_create_pool(endpt, "ws", 512, 512))) {
@@ -219,6 +224,7 @@ static int transport_create(void *data)
newtransport->transport.flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)newtransport->transport.key.type);
newtransport->transport.info = (char *)pj_pool_alloc(newtransport->transport.pool, 64);
+ newtransport->transport.dir = PJSIP_TP_DIR_INCOMING;
newtransport->transport.tpmgr = tpmgr;
newtransport->transport.send_msg = &ws_send_msg;
newtransport->transport.destroy = &ws_destroy;
@@ -242,6 +248,16 @@ static int transport_create(void *data)
}
create_data->transport = newtransport;
+
+ /* Notify application of transport state */
+ state_cb = pjsip_tpmgr_get_state_cb(newtransport->transport.tpmgr);
+ if (state_cb) {
+ pjsip_transport_state_info state_info;
+
+ memset(&state_info, 0, sizeof(state_info));
+ state_cb(&newtransport->transport, PJSIP_TP_STATE_CONNECTED, &state_info);
+ }
+
return 0;
on_error:
@@ -302,10 +318,14 @@ static int get_write_timeout(void)
for (; (transport_state = ao2_iterator_next(&it_transport_states)); ao2_cleanup(transport_state)) {
struct ast_sip_transport *transport;
+
if (transport_state->type != AST_TRANSPORT_WS && transport_state->type != AST_TRANSPORT_WSS) {
continue;
}
transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", transport_state->id);
+ if (!transport) {
+ continue;
+ }
ast_debug(5, "Found %s transport with write timeout: %d\n",
transport->type == AST_TRANSPORT_WS ? "WS" : "WSS",
transport->write_timeout);
@@ -361,6 +381,7 @@ static void websocket_cb(struct ast_websocket *session, struct ast_variable *par
if (ast_sip_push_task_synchronous(serializer, transport_create, &create_data)) {
ast_log(LOG_ERROR, "Could not create WebSocket transport.\n");
+ ast_taskprocessor_unreference(serializer);
ast_websocket_unref(session);
return;
}
diff --git a/res/res_pjsip_xpidf_body_generator.c b/res/res_pjsip_xpidf_body_generator.c
index 298235cbc..0977159ee 100644
--- a/res/res_pjsip_xpidf_body_generator.c
+++ b/res/res_pjsip_xpidf_body_generator.c
@@ -63,7 +63,7 @@ static int xpidf_generate_body_content(void *body, void *data)
pj_xml_node *msnsubstatus;
ast_sip_presence_exten_state_to_str(state_data->exten_state, &statestring,
- &pidfstate, &pidfnote, &local_state);
+ &pidfstate, &pidfnote, &local_state, 0);
ast_sip_presence_xml_find_node_attr(state_data->pool, pres, "atom", "id",
&atom, &attr);
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index f75482f7f..77027aafe 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -218,8 +218,9 @@ static AST_RWLIST_HEAD_STATIC(host_candidates, ast_ice_host_candidate);
/*! \brief RTP learning mode tracking information */
struct rtp_learning_info {
- int max_seq; /*!< The highest sequence number received */
- int packets; /*!< The number of remaining packets before the source is accepted */
+ int max_seq; /*!< The highest sequence number received */
+ int packets; /*!< The number of remaining packets before the source is accepted */
+ struct timeval received; /*!< The time of the last received packet */
};
#ifdef HAVE_OPENSSL_SRTP
@@ -302,6 +303,7 @@ struct ast_rtp {
void *data;
struct ast_rtcp *rtcp;
struct ast_rtp *bridged; /*!< Who we are Packet bridged to */
+ unsigned int asymmetric_codec; /*!< Indicate if asymmetric send/receive codecs are allowed */
enum strict_rtp_state strict_rtp_state; /*!< Current state that strict RTP protection is in */
struct ast_sockaddr strict_rtp_address; /*!< Remote address information for strict RTP purposes */
@@ -311,7 +313,6 @@ struct ast_rtp {
* but these are in place to keep learning mode sequence values sealed from their normal counterparts.
*/
struct rtp_learning_info rtp_source_learn; /* Learning mode track for the expected RTP source */
- struct rtp_learning_info alt_source_learn; /* Learning mode tracking for a new RTP source after one has been chosen */
struct rtp_red *red;
@@ -319,6 +320,7 @@ struct ast_rtp {
ast_cond_t cond; /*!< ICE/TURN condition for signaling */
struct ice_wrap *ice; /*!< ao2 wrapped ICE session */
+ enum ast_rtp_ice_role role; /*!< Our role in ICE negotiation */
pj_turn_sock *turn_rtp; /*!< RTP TURN relay */
pj_turn_sock *turn_rtcp; /*!< RTCP TURN relay */
pj_turn_state_t turn_state; /*!< Current state of the TURN relay session */
@@ -677,11 +679,41 @@ static void ice_wrap_dtor(void *vdoomed)
}
}
+static void ast2pj_rtp_ice_role(enum ast_rtp_ice_role ast_role, enum pj_ice_sess_role *pj_role)
+{
+ switch (ast_role) {
+ case AST_RTP_ICE_ROLE_CONTROLLED:
+ *pj_role = PJ_ICE_SESS_ROLE_CONTROLLED;
+ break;
+ case AST_RTP_ICE_ROLE_CONTROLLING:
+ *pj_role = PJ_ICE_SESS_ROLE_CONTROLLING;
+ break;
+ }
+}
+
+static void pj2ast_rtp_ice_role(enum pj_ice_sess_role pj_role, enum ast_rtp_ice_role *ast_role)
+{
+ switch (pj_role) {
+ case PJ_ICE_SESS_ROLE_CONTROLLED:
+ *ast_role = AST_RTP_ICE_ROLE_CONTROLLED;
+ return;
+ case PJ_ICE_SESS_ROLE_CONTROLLING:
+ *ast_role = AST_RTP_ICE_ROLE_CONTROLLING;
+ return;
+ case PJ_ICE_SESS_ROLE_UNKNOWN:
+ /* Don't change anything */
+ return;
+ default:
+ /* If we aren't explicitly handling something, it's a bug */
+ ast_assert(0);
+ return;
+ }
+}
+
/*! \pre instance is locked */
static int ice_reset_session(struct ast_rtp_instance *instance)
{
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
- pj_ice_sess_role role = rtp->ice->real_ice->role;
int res;
ast_debug(3, "Resetting ICE for RTP instance '%p'\n", instance);
@@ -693,7 +725,9 @@ static int ice_reset_session(struct ast_rtp_instance *instance)
ast_debug(3, "Recreating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(&rtp->ice_original_rtp_addr), rtp->ice_port, instance);
res = ice_create(instance, &rtp->ice_original_rtp_addr, rtp->ice_port, 1);
if (!res) {
- /* Preserve the role that the old ICE session used */
+ /* Use the current expected role for the ICE session */
+ enum pj_ice_sess_role role = PJ_ICE_SESS_ROLE_UNKNOWN;
+ ast2pj_rtp_ice_role(rtp->role, &role);
pj_ice_sess_change_role(rtp->ice->real_ice, role);
}
@@ -765,6 +799,8 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
ast_debug(3, "Proposed == active candidates for RTP instance '%p'\n", instance);
ao2_cleanup(rtp->ice_proposed_remote_candidates);
rtp->ice_proposed_remote_candidates = NULL;
+ /* If this ICE session is being preserved then go back to the role it currently is */
+ pj2ast_rtp_ice_role(rtp->ice->real_ice->role, &rtp->role);
return;
}
@@ -938,10 +974,7 @@ static void ast_rtp_ice_set_role(struct ast_rtp_instance *instance, enum ast_rtp
return;
}
- pj_thread_register_check();
-
- pj_ice_sess_change_role(rtp->ice->real_ice, role == AST_RTP_ICE_ROLE_CONTROLLED ?
- PJ_ICE_SESS_ROLE_CONTROLLED : PJ_ICE_SESS_ROLE_CONTROLLING);
+ rtp->role = role;
}
/*! \pre instance is locked */
@@ -1291,6 +1324,8 @@ static void ast_rtp_ice_turn_request(struct ast_rtp_instance *instance, enum ast
pj_turn_session_info info;
struct ast_sockaddr local, loop;
pj_status_t status;
+ pj_turn_sock_cfg turn_sock_cfg;
+ struct ice_wrap *ice;
ast_rtp_instance_get_local_address(instance, &local);
if (ast_sockaddr_is_ipv4(&local)) {
@@ -1353,11 +1388,20 @@ static void ast_rtp_ice_turn_request(struct ast_rtp_instance *instance, enum ast
pj_stun_config_init(&stun_config, &cachingpool.factory, 0, rtp->ioqueue->ioqueue, rtp->ioqueue->timerheap);
+ /* Use ICE session group lock for TURN session to avoid deadlock */
+ pj_turn_sock_cfg_default(&turn_sock_cfg);
+ ice = rtp->ice;
+ if (ice) {
+ turn_sock_cfg.grp_lock = ice->real_ice->grp_lock;
+ ao2_ref(ice, +1);
+ }
+
/* Release the instance lock to avoid deadlock with PJPROJECT group lock */
ao2_unlock(instance);
status = pj_turn_sock_create(&stun_config,
ast_sockaddr_is_ipv4(&addr) ? pj_AF_INET() : pj_AF_INET6(), conn_type,
- turn_cb, NULL, instance, turn_sock);
+ turn_cb, &turn_sock_cfg, instance, turn_sock);
+ ao2_cleanup(ice);
if (status != PJ_SUCCESS) {
ast_log(LOG_WARNING, "Could not create a TURN client socket\n");
ao2_lock(instance);
@@ -2522,6 +2566,17 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
return -1;
}
if (!rtp->passthrough) {
+ /* If a unidirectional ICE negotiation occurs then lock on to the source of the
+ * ICE traffic and use it as the target. This will occur if the remote side only
+ * wants to receive media but never send to us.
+ */
+ if (!rtp->ice_active_remote_candidates && !rtp->ice_proposed_remote_candidates) {
+ if (rtcp) {
+ ast_sockaddr_copy(&rtp->rtcp->them, sa);
+ } else {
+ ast_rtp_instance_set_remote_address(instance, sa);
+ }
+ }
return 0;
}
rtp->passthrough = 0;
@@ -2565,17 +2620,22 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
#ifdef HAVE_PJPROJECT
if (rtp->ice) {
+ enum ast_rtp_ice_component_type component = rtcp ? AST_RTP_ICE_COMPONENT_RTCP : AST_RTP_ICE_COMPONENT_RTP;
pj_status_t status;
struct ice_wrap *ice;
+ /* If RTCP is sharing the same socket then use the same component */
+ if (rtcp && rtp->rtcp->s == rtp->s) {
+ component = AST_RTP_ICE_COMPONENT_RTP;
+ }
+
pj_thread_register_check();
/* Release the instance lock to avoid deadlock with PJPROJECT group lock */
ice = rtp->ice;
ao2_ref(ice, +1);
ao2_unlock(instance);
- status = pj_ice_sess_send_data(ice->real_ice,
- rtcp ? AST_RTP_ICE_COMPONENT_RTCP : AST_RTP_ICE_COMPONENT_RTP, temp, len);
+ status = pj_ice_sess_send_data(ice->real_ice, component, temp, len);
ao2_ref(ice, -1);
ao2_lock(instance);
if (status == PJ_SUCCESS) {
@@ -2695,6 +2755,7 @@ static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq)
{
info->max_seq = seq - 1;
info->packets = learning_min_sequential;
+ memset(&info->received, 0, sizeof(info->received));
}
/*!
@@ -2709,6 +2770,13 @@ static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq)
*/
static int rtp_learning_rtp_seq_update(struct rtp_learning_info *info, uint16_t seq)
{
+ if (!ast_tvzero(info->received) && ast_tvdiff_ms(ast_tvnow(), info->received) < 5) {
+ /* During the probation period the minimum amount of media we'll accept is
+ * 10ms so give a reasonable 5ms buffer just in case we get it sporadically.
+ */
+ return 1;
+ }
+
if (seq == info->max_seq + 1) {
/* packet is in sequence */
info->packets--;
@@ -2717,6 +2785,7 @@ static int rtp_learning_rtp_seq_update(struct rtp_learning_info *info, uint16_t
info->packets = learning_min_sequential - 1;
}
info->max_seq = seq;
+ info->received = ast_tvnow();
return (info->packets == 0);
}
@@ -2991,10 +3060,9 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
/* Set default parameters on the newly created RTP structure */
rtp->ssrc = ast_random();
rtp->seqno = ast_random() & 0x7fff;
- rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_LEARN : STRICT_RTP_OPEN);
+ rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_CLOSED : STRICT_RTP_OPEN);
if (strictrtp) {
rtp_learning_seq_init(&rtp->rtp_source_learn, (uint16_t)rtp->seqno);
- rtp_learning_seq_init(&rtp->alt_source_learn, (uint16_t)rtp->seqno);
}
/* Create a new socket for us to listen on and use */
@@ -3443,7 +3511,7 @@ static void ast_rtp_change_source(struct ast_rtp_instance *instance)
ast_debug(3, "Changing ssrc for SRTP from %u to %u\n", rtp->ssrc, ssrc);
res_srtp->change_source(srtp, rtp->ssrc, ssrc);
if (rtcp_srtp != srtp) {
- res_srtp->change_source(srtp, rtp->ssrc, ssrc);
+ res_srtp->change_source(rtcp_srtp, rtp->ssrc, ssrc);
}
}
@@ -4535,17 +4603,6 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
packetwords = size / 4;
- if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) {
- /* Send to whoever sent to us */
- if (ast_sockaddr_cmp(&rtp->rtcp->them, addr)) {
- ast_sockaddr_copy(&rtp->rtcp->them, addr);
- if (rtpdebug) {
- ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s\n",
- ast_sockaddr_stringify(&rtp->rtcp->them));
- }
- }
- }
-
ast_debug(1, "Got RTCP report of %zu bytes\n", size);
while (position < packetwords) {
@@ -4574,6 +4631,25 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
return &ast_null_frame;
}
+ if ((rtp->strict_rtp_state != STRICT_RTP_OPEN) && (rtcp_report->ssrc != rtp->themssrc)) {
+ /* Skip over this RTCP record as it does not contain the correct SSRC */
+ position += (length + 1);
+ ast_debug(1, "%p -- Received RTCP report from %s, dropping due to strict RTP protection. Received SSRC '%u' but expected '%u'\n",
+ rtp, ast_sockaddr_stringify(addr), rtcp_report->ssrc, rtp->themssrc);
+ continue;
+ }
+
+ if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) {
+ /* Send to whoever sent to us */
+ if (ast_sockaddr_cmp(&rtp->rtcp->them, addr)) {
+ ast_sockaddr_copy(&rtp->rtcp->them, addr);
+ if (rtpdebug) {
+ ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s\n",
+ ast_sockaddr_stringify(&rtp->rtcp->them));
+ }
+ }
+ }
+
if (rtcp_debug_test_addr(addr)) {
ast_verbose("\n\nGot RTCP from %s\n",
ast_sockaddr_stringify(addr));
@@ -4795,9 +4871,6 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance,
return -1;
}
- rtp->rxcount++;
- rtp->rxoctetcount += (len - hdrlen);
-
/* If the payload coming in is not one of the negotiated ones then send it to the core, this will cause formats to change and the bridge to break */
if (ast_rtp_codecs_find_payload_code(ast_rtp_instance_get_codecs(instance1), bridged_payload) == -1) {
ast_debug(1, "Unsupported payload type received \n");
@@ -4820,6 +4893,23 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance,
return -1;
}
+
+ ao2_replace(rtp->lastrxformat, payload_type->format);
+ ao2_replace(bridged->lasttxformat, payload_type->format);
+
+ /*
+ * If bridged peer has already received rtp, perform the asymmetric codec check
+ * if that feature has been activated
+ */
+ if (!bridged->asymmetric_codec && bridged->lastrxformat != ast_format_none) {
+ if (ast_format_cmp(bridged->lasttxformat, bridged->lastrxformat) == AST_FORMAT_CMP_NOT_EQUAL) {
+ ast_debug(1, "Asymmetric RTP codecs detected (TX: %s, RX: %s) sending frame to core\n",
+ ast_format_get_name(bridged->lasttxformat),
+ ast_format_get_name(bridged->lastrxformat));
+ return -1;
+ }
+ }
+
/* If the marker bit has been explicitly set turn it on */
if (ast_test_flag(rtp, FLAG_NEED_MARKER_BIT)) {
mark = 1;
@@ -4980,39 +5070,37 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
return &ast_null_frame;
}
+ /* If the version is not what we expected by this point then just drop the packet */
+ if (version != 2) {
+ return &ast_null_frame;
+ }
+
/* If strict RTP protection is enabled see if we need to learn the remote address or if we need to drop the packet */
if (rtp->strict_rtp_state == STRICT_RTP_LEARN) {
- ast_debug(1, "%p -- Probation learning mode pass with source address %s\n", rtp, ast_sockaddr_stringify(&addr));
- /* For now, we always copy the address. */
- ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
-
- /* Send the rtp and the seqno from header to rtp_learning_rtp_seq_update to see whether we can exit or not*/
- if (rtp_learning_rtp_seq_update(&rtp->rtp_source_learn, seqno)) {
- ast_debug(1, "%p -- Probation at seq %d with %d to go; discarding frame\n",
- rtp, rtp->rtp_source_learn.max_seq, rtp->rtp_source_learn.packets);
- return &ast_null_frame;
- }
-
- ast_verb(4, "%p -- Probation passed - setting RTP source address to %s\n", rtp, ast_sockaddr_stringify(&addr));
- rtp->strict_rtp_state = STRICT_RTP_CLOSED;
- }
- if (rtp->strict_rtp_state == STRICT_RTP_CLOSED) {
if (!ast_sockaddr_cmp(&rtp->strict_rtp_address, &addr)) {
- /* Always reset the alternate learning source */
- rtp_learning_seq_init(&rtp->alt_source_learn, seqno);
+ /* We are learning a new address but have received traffic from the existing address,
+ * accept it but reset the current learning for the new source so it only takes over
+ * once sufficient traffic has been received. */
+ rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
} else {
/* Start trying to learn from the new address. If we pass a probationary period with
* it, that means we've stopped getting RTP from the original source and we should
* switch to it.
*/
- if (rtp_learning_rtp_seq_update(&rtp->alt_source_learn, seqno)) {
+ if (rtp_learning_rtp_seq_update(&rtp->rtp_source_learn, seqno)) {
ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection. Will switch to it in %d packets\n",
- rtp, ast_sockaddr_stringify(&addr), rtp->alt_source_learn.packets);
+ rtp, ast_sockaddr_stringify(&addr), rtp->rtp_source_learn.packets);
return &ast_null_frame;
}
- ast_verb(4, "%p -- Switching RTP source address to %s\n", rtp, ast_sockaddr_stringify(&addr));
ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
+
+ ast_verb(4, "%p -- Probation passed - setting RTP source address to %s\n", rtp, ast_sockaddr_stringify(&addr));
+ rtp->strict_rtp_state = STRICT_RTP_CLOSED;
}
+ } else if (rtp->strict_rtp_state == STRICT_RTP_CLOSED && ast_sockaddr_cmp(&rtp->strict_rtp_address, &addr)) {
+ ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection.\n",
+ rtp, ast_sockaddr_stringify(&addr));
+ return &ast_null_frame;
}
/* If symmetric RTP is enabled see if the remote side is not what we expected and change where we are sending audio */
@@ -5033,18 +5121,6 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
}
}
- /* If we are directly bridged to another instance send the audio directly out */
- instance1 = ast_rtp_instance_get_bridged(instance);
- if (instance1
- && !bridge_p2p_rtp_write(instance, instance1, rtpheader, res, hdrlen)) {
- return &ast_null_frame;
- }
-
- /* If the version is not what we expected by this point then just drop the packet */
- if (version != 2) {
- return &ast_null_frame;
- }
-
/* Pull out the various other fields we will need */
payloadtype = (seqno & 0x7f0000) >> 16;
padding = seqno & (1 << 29);
@@ -5144,6 +5220,28 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
rtp->themssrc = ntohl(rtpheader[2]); /* Record their SSRC to put in future RR */
}
+
+ /* If we are directly bridged to another instance send the audio directly out,
+ * but only after updating core information about the received traffic so that
+ * outgoing RTCP reflects it.
+ */
+ instance1 = ast_rtp_instance_get_bridged(instance);
+ if (instance1
+ && !bridge_p2p_rtp_write(instance, instance1, rtpheader, res, hdrlen)) {
+ struct timeval rxtime;
+ struct ast_frame *f;
+
+ /* Update statistics for jitter so they are correct in RTCP */
+ calc_rxstamp(&rxtime, rtp, timestamp, mark);
+
+ /* When doing P2P we don't need to raise any frames about SSRC change to the core */
+ while ((f = AST_LIST_REMOVE_HEAD(&frames, frame_list)) != NULL) {
+ ast_frfree(f);
+ }
+
+ return &ast_null_frame;
+ }
+
if (rtp_debug_test_addr(&addr)) {
ast_verbose("Got RTP packet from %s (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6d)\n",
ast_sockaddr_stringify(&addr),
@@ -5472,6 +5570,8 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
rtp->rtcp = NULL;
}
}
+ } else if (property == AST_RTP_PROPERTY_ASYMMETRIC_CODEC) {
+ rtp->asymmetric_codec = value;
}
}
@@ -5518,7 +5618,11 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct
rtp->rxseqno = 0;
- if (strictrtp && rtp->strict_rtp_state != STRICT_RTP_OPEN) {
+ if (strictrtp && rtp->strict_rtp_state != STRICT_RTP_OPEN && !ast_sockaddr_isnull(addr) &&
+ ast_sockaddr_cmp(addr, &rtp->strict_rtp_address)) {
+ /* We only need to learn a new strict source address if we've been told the source is
+ * changing to something different.
+ */
rtp->strict_rtp_state = STRICT_RTP_LEARN;
rtp_learning_seq_init(&rtp->rtp_source_learn, rtp->seqno);
}
diff --git a/res/res_smdi.c b/res/res_smdi.c
index f4804c7cb..9a40227f2 100644
--- a/res/res_smdi.c
+++ b/res/res_smdi.c
@@ -610,13 +610,12 @@ static void *smdi_read(void *iface_p)
ast_debug(1, "Read a 'D' ... it's an MD message.\n");
- if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
+ md_msg = ao2_alloc(sizeof(*md_msg), NULL);
+ if (!md_msg) {
ao2_ref(iface, -1);
return NULL;
}
- md_msg = ao2_alloc(sizeof(*md_msg), NULL);
-
/* read the message desk number */
for (i = 0; i < sizeof(md_msg->mesg_desk_num) - 1; i++) {
md_msg->mesg_desk_num[i] = fgetc(iface->file);
@@ -712,13 +711,12 @@ static void *smdi_read(void *iface_p)
ast_debug(1, "Read a 'W', it's an MWI message. (No more debug coming for MWI messages)\n");
- if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
+ mwi_msg = ao2_alloc(sizeof(*mwi_msg), NULL);
+ if (!mwi_msg) {
ao2_ref(iface, -1);
return NULL;
}
- mwi_msg = ao2_alloc(sizeof(*mwi_msg), NULL);
-
/* discard the 'I' (from 'MWI') */
fgetc(iface->file);
diff --git a/res/res_stasis.c b/res/res_stasis.c
index 9d7bc4c24..b1cea3ad5 100644
--- a/res/res_stasis.c
+++ b/res/res_stasis.c
@@ -1057,8 +1057,18 @@ static void channel_stolen_cb(void *data, struct ast_channel *old_chan, struct a
{
struct stasis_app_control *control;
- /* find control */
- control = ao2_callback(app_controls, 0, masq_match_cb, old_chan);
+ /*
+ * At this point, old_chan is the channel pointer that is in Stasis() and
+ * has the unknown channel's name in it while new_chan is the channel pointer
+ * that is not in Stasis(), but has the guts of the channel that Stasis() knows
+ * about.
+ *
+ * Find and unlink control since the channel has a new name/uniqueid
+ * and its hash has changed. Since the channel is leaving stasis don't
+ * bother putting it back into the container. Nobody is going to
+ * remove it from the container later.
+ */
+ control = ao2_callback(app_controls, OBJ_UNLINK, masq_match_cb, old_chan);
if (!control) {
ast_log(LOG_ERROR, "Could not find control for masqueraded channel\n");
return;
@@ -1099,8 +1109,10 @@ static void channel_replaced_cb(void *data, struct ast_channel *old_chan, struct
return;
}
- /* find, unlink, and relink control since the channel has a new name and
- * its hash has likely changed */
+ /*
+ * Find, unlink, and relink control since the channel has a new
+ * name/uniqueid and its hash has changed.
+ */
control = ao2_callback(app_controls, OBJ_UNLINK, masq_match_cb, new_chan);
if (!control) {
ast_log(LOG_ERROR, "Could not find control for masquerading channel\n");
diff --git a/res/res_stasis_device_state.c b/res/res_stasis_device_state.c
index 29e75660c..51101dd7b 100644
--- a/res/res_stasis_device_state.c
+++ b/res/res_stasis_device_state.c
@@ -108,7 +108,6 @@ static int device_state_subscriptions_cmp(void *obj, void *arg, int flags)
static void device_state_subscription_destroy(void *obj)
{
struct device_state_subscription *sub = obj;
- sub->sub = stasis_unsubscribe_and_join(sub->sub);
ast_string_field_free_memory(sub);
}
@@ -154,6 +153,9 @@ static struct device_state_subscription *find_device_state_subscription(
static void remove_device_state_subscription(
struct device_state_subscription *sub)
{
+ if (sub->sub) {
+ sub->sub = stasis_unsubscribe_and_join(sub->sub);
+ }
ao2_unlink_flags(device_state_subscriptions, sub, OBJ_NOLOCK);
}
diff --git a/res/res_stasis_snoop.c b/res/res_stasis_snoop.c
index abdef6e46..da66894f6 100644
--- a/res/res_stasis_snoop.c
+++ b/res/res_stasis_snoop.c
@@ -74,6 +74,8 @@ struct stasis_app_snoop {
unsigned int whisper_active:1;
/*! \brief Uniqueid of the channel this snoop is snooping on */
char uniqueid[AST_MAX_UNIQUEID];
+ /*! \brief A frame of silence to use when the audiohook returns null */
+ struct ast_frame silence;
};
/*! \brief Destructor for snoop structure */
@@ -93,6 +95,11 @@ static void snoop_destroy(void *obj)
ast_audiohook_destroy(&snoop->whisper);
}
+ if (snoop->silence.data.ptr) {
+ ast_free(snoop->silence.data.ptr);
+ snoop->silence.data.ptr = NULL;
+ }
+
ast_free(snoop->app);
ast_channel_cleanup(snoop->chan);
@@ -199,7 +206,7 @@ static struct ast_frame *snoop_read(struct ast_channel *chan)
frame = ast_audiohook_read_frame(&snoop->spy, snoop->spy_samples, snoop->spy_direction, snoop->spy_format);
ast_audiohook_unlock(&snoop->spy);
- return frame ? frame : &ast_null_frame;
+ return frame ? frame : &snoop->silence;
}
/*! \brief Callback function for hanging up a Snoop channel */
@@ -385,6 +392,19 @@ struct ast_channel *stasis_app_control_snoop(struct ast_channel *chan,
snoop->spy_samples = ast_format_get_sample_rate(snoop->spy_format) / (1000 / SNOOP_INTERVAL);
snoop->spy_active = 1;
+
+ snoop->silence.frametype = AST_FRAME_VOICE,
+ snoop->silence.datalen = snoop->spy_samples * sizeof(uint16_t),
+ snoop->silence.samples = snoop->spy_samples,
+ snoop->silence.mallocd = 0,
+ snoop->silence.offset = 0,
+ snoop->silence.src = __PRETTY_FUNCTION__,
+ snoop->silence.subclass.format = snoop->spy_format,
+ snoop->silence.data.ptr = ast_calloc(snoop->spy_samples, sizeof(uint16_t));
+ if (!snoop->silence.data.ptr) {
+ ast_hangup(snoop->chan);
+ return NULL;
+ }
}
/* If whispering is enabled set up the audiohook */
diff --git a/res/res_xmpp.c b/res/res_xmpp.c
index 95d3cc009..d8487f5ba 100644
--- a/res/res_xmpp.c
+++ b/res/res_xmpp.c
@@ -61,6 +61,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/manager.h"
#include "asterisk/cli.h"
#include "asterisk/config_options.h"
+#include "asterisk/json.h"
/*** DOCUMENTATION
<application name="JabberSend" language="en_US" module="res_xmpp">
@@ -323,6 +324,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<configOption name="secret">
<synopsis>XMPP password</synopsis>
</configOption>
+ <configOption name="refresh_token">
+ <synopsis>Google OAuth 2.0 refresh token</synopsis>
+ </configOption>
+ <configOption name="oauth_clientid">
+ <synopsis>Google OAuth 2.0 application's client id</synopsis>
+ </configOption>
+ <configOption name="oauth_secret">
+ <synopsis>Google OAuth 2.0 application's secret</synopsis>
+ </configOption>
<configOption name="serverhost">
<synopsis>Route to server, e.g. talk.google.com</synopsis>
</configOption>
@@ -461,6 +471,9 @@ struct ast_xmpp_client_config {
AST_STRING_FIELD(name); /*!< Name of the client connection */
AST_STRING_FIELD(user); /*!< Username to use for authentication */
AST_STRING_FIELD(password); /*!< Password to use for authentication */
+ AST_STRING_FIELD(refresh_token); /*!< Refresh token to use for OAuth authentication */
+ AST_STRING_FIELD(oauth_clientid); /*!< Client ID to use for OAuth authentication */
+ AST_STRING_FIELD(oauth_secret); /*!< Secret to use for OAuth authentication */
AST_STRING_FIELD(server); /*!< Server hostname */
AST_STRING_FIELD(statusmsg); /*!< Status message for presence */
AST_STRING_FIELD(pubsubnode); /*!< Pubsub node */
@@ -529,6 +542,7 @@ static ast_cond_t message_received_condition;
static ast_mutex_t messagelock;
static int xmpp_client_config_post_apply(void *obj, void *arg, int flags);
+static int fetch_access_token(struct ast_xmpp_client_config *cfg);
/*! \brief Destructor function for configuration */
static void ast_xmpp_client_config_destructor(void *obj)
@@ -761,12 +775,16 @@ static int xmpp_config_prelink(void *newitem)
if (ast_strlen_zero(clientcfg->user)) {
ast_log(LOG_ERROR, "No user specified on client '%s'\n", clientcfg->name);
return -1;
- } else if (ast_strlen_zero(clientcfg->password)) {
- ast_log(LOG_ERROR, "No password specified on client '%s'\n", clientcfg->name);
+ } else if (ast_strlen_zero(clientcfg->password) && ast_strlen_zero(clientcfg->refresh_token)) {
+ ast_log(LOG_ERROR, "No password or refresh_token specified on client '%s'\n", clientcfg->name);
return -1;
} else if (ast_strlen_zero(clientcfg->server)) {
ast_log(LOG_ERROR, "No server specified on client '%s'\n", clientcfg->name);
return -1;
+ } else if (!ast_strlen_zero(clientcfg->refresh_token) &&
+ (ast_strlen_zero(clientcfg->oauth_clientid) || ast_strlen_zero(clientcfg->oauth_secret))) {
+ ast_log(LOG_ERROR, "No oauth_clientid or oauth_secret specified, so client '%s' can't be used\n", clientcfg->name);
+ return -1;
}
/* If this is a new connection force a reconnect */
@@ -778,6 +796,9 @@ static int xmpp_config_prelink(void *newitem)
/* If any configuration options are changing that would require reconnecting set the bit so we will do so if possible */
if (strcmp(clientcfg->user, oldclientcfg->user) ||
strcmp(clientcfg->password, oldclientcfg->password) ||
+ strcmp(clientcfg->refresh_token, oldclientcfg->refresh_token) ||
+ strcmp(clientcfg->oauth_clientid, oldclientcfg->oauth_clientid) ||
+ strcmp(clientcfg->oauth_secret, oldclientcfg->oauth_secret) ||
strcmp(clientcfg->server, oldclientcfg->server) ||
(clientcfg->port != oldclientcfg->port) ||
(ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) != ast_test_flag(&oldclientcfg->flags, XMPP_COMPONENT)) ||
@@ -2786,7 +2807,13 @@ static int xmpp_client_authenticate_sasl(struct ast_xmpp_client *client, struct
}
iks_insert_attrib(auth, "xmlns", IKS_NS_XMPP_SASL);
- iks_insert_attrib(auth, "mechanism", "PLAIN");
+ if (!ast_strlen_zero(cfg->refresh_token)) {
+ iks_insert_attrib(auth, "mechanism", "X-OAUTH2");
+ iks_insert_attrib(auth, "auth:service", "oauth2");
+ iks_insert_attrib(auth, "xmlns:auth", "http://www.google.com/talk/protocol/auth");
+ } else {
+ iks_insert_attrib(auth, "mechanism", "PLAIN");
+ }
if (strchr(client->jid->user, '/')) {
char *user = ast_strdupa(client->jid->user);
@@ -3285,28 +3312,28 @@ static int xmpp_ping_request(struct ast_xmpp_client *client, const char *to, con
{
iks *iq, *ping;
int res;
-
+
ast_debug(2, "JABBER: Sending Keep-Alive Ping for client '%s'\n", client->name);
if (!(iq = iks_new("iq")) || !(ping = iks_new("ping"))) {
iks_delete(iq);
return -1;
}
-
+
iks_insert_attrib(iq, "type", "get");
iks_insert_attrib(iq, "to", to);
iks_insert_attrib(iq, "from", from);
-
+
ast_xmpp_client_lock(client);
iks_insert_attrib(iq, "id", client->mid);
ast_xmpp_increment_mid(client->mid);
ast_xmpp_client_unlock(client);
-
+
iks_insert_attrib(ping, "xmlns", "urn:xmpp:ping");
iks_insert_node(iq, ping);
-
+
res = ast_xmpp_client_send(client, iq);
-
+
iks_delete(ping);
iks_delete(iq);
@@ -3627,6 +3654,13 @@ static int xmpp_client_reconnect(struct ast_xmpp_client *client)
return -1;
}
+ if (!ast_strlen_zero(clientcfg->refresh_token)) {
+ ast_debug(2, "Obtaining OAuth access token for client '%s'\n", client->name);
+ if (fetch_access_token(clientcfg)) {
+ return -1;
+ }
+ }
+
ast_xmpp_client_disconnect(client);
client->timeout = 50;
@@ -3643,7 +3677,7 @@ static int xmpp_client_reconnect(struct ast_xmpp_client *client)
/* Set socket timeout options */
setsockopt(iks_fd(client->parser), SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval));
-
+
if (res == IKS_NET_NOCONN) {
ast_log(LOG_ERROR, "No XMPP connection available when trying to connect client '%s'\n", client->name);
return -1;
@@ -3728,7 +3762,7 @@ static int xmpp_client_receive(struct ast_xmpp_client *client, unsigned int time
/* Log the message here, because iksemel's logHook is
unaccessible */
xmpp_log_hook(client, buf, len, 1);
-
+
if(buf[0] == ' ') {
ast_debug(1, "JABBER: Detected Google Keep Alive. "
"Sending out Ping request for client '%s'\n", client->name);
@@ -3869,6 +3903,42 @@ static int xmpp_client_config_merge_buddies(void *obj, void *arg, int flags)
return 1;
}
+static int fetch_access_token(struct ast_xmpp_client_config *cfg)
+{
+ RAII_VAR(char *, cmd, NULL, ast_free);
+ char cBuf[1024] = "";
+ const char *url = "https://www.googleapis.com/oauth2/v3/token";
+ struct ast_json_error error;
+ RAII_VAR(struct ast_json *, jobj, NULL, ast_json_unref);
+
+ ast_asprintf(&cmd, "CURL(%s,client_id=%s&client_secret=%s&refresh_token=%s&grant_type=refresh_token)",
+ url, cfg->oauth_clientid, cfg->oauth_secret, cfg->refresh_token);
+
+ ast_debug(2, "Performing OAuth 2.0 authentication for client '%s' using command: %s\n",
+ cfg->name, cmd);
+
+ if (ast_func_read(NULL, cmd, cBuf, sizeof(cBuf) - 1)) {
+ ast_log(LOG_ERROR, "CURL is unavailable. This is required for OAuth 2.0 authentication of XMPP client '%s'. Please ensure it is loaded.\n",
+ cfg->name);
+ return -1;
+ }
+
+ ast_debug(2, "OAuth 2.0 authentication for client '%s' returned: %s\n", cfg->name, cBuf);
+
+ jobj = ast_json_load_string(cBuf, &error);
+ if (jobj) {
+ const char *token = ast_json_string_get(ast_json_object_get(jobj, "access_token"));
+ if (token) {
+ ast_string_field_set(cfg, password, token);
+ return 0;
+ }
+ }
+
+ ast_log(LOG_ERROR, "An error occurred while performing OAuth 2.0 authentication for client '%s': %s\n", cfg->name, cBuf);
+
+ return -1;
+}
+
static int xmpp_client_config_post_apply(void *obj, void *arg, int flags)
{
struct ast_xmpp_client_config *cfg = obj;
@@ -4622,8 +4692,8 @@ static int client_buddy_handler(const struct aco_option *opt, struct ast_variabl
* Module loading including tests for configuration or dependencies.
* This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
* or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
- * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
- * configuration file or other non-critical problem return
+ * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
+ * configuration file or other non-critical problem return
* AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
*/
static int load_module(void)
@@ -4641,6 +4711,9 @@ static int load_module(void)
aco_option_register(&cfg_info, "username", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, user));
aco_option_register(&cfg_info, "secret", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, password));
+ aco_option_register(&cfg_info, "refresh_token", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, refresh_token));
+ aco_option_register(&cfg_info, "oauth_clientid", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, oauth_clientid));
+ aco_option_register(&cfg_info, "oauth_secret", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, oauth_secret));
aco_option_register(&cfg_info, "serverhost", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, server));
aco_option_register(&cfg_info, "statusmessage", ACO_EXACT, client_options, "Online and Available", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, statusmsg));
aco_option_register(&cfg_info, "pubsub_node", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, pubsubnode));
diff --git a/res/srtp/srtp_compat.h b/res/srtp/srtp_compat.h
index 4ab39f318..bf4208244 100644
--- a/res/srtp/srtp_compat.h
+++ b/res/srtp/srtp_compat.h
@@ -5,7 +5,11 @@
#define crypto_policy_t srtp_crypto_policy_t
+#if defined(SRTP_AES_ICM_128)
+#define AES_128_ICM SRTP_AES_ICM_128
+#else
#define AES_128_ICM SRTP_AES_ICM
+#endif
#define HMAC_SHA1 SRTP_HMAC_SHA1
#define err_status_t srtp_err_status_t
diff --git a/res/stasis/control.c b/res/stasis/control.c
index b2b076b73..7e8ea91ef 100644
--- a/res/stasis/control.c
+++ b/res/stasis/control.c
@@ -927,14 +927,21 @@ static int bridge_channel_depart(struct stasis_app_control *control,
return 0;
}
-static void bridge_after_cb(struct ast_channel *chan, void *data)
+static void internal_bridge_after_cb(struct ast_channel *chan, void *data,
+ enum ast_bridge_after_cb_reason reason)
{
struct stasis_app_control *control = data;
SCOPED_AO2LOCK(lock, control);
struct ast_bridge_channel *bridge_channel;
- ast_debug(3, "%s, %s: Channel leaving bridge\n",
- ast_channel_uniqueid(chan), control->bridge->uniqueid);
+ ast_debug(3, "%s, %s: %s\n",
+ ast_channel_uniqueid(chan), control->bridge ? control->bridge->uniqueid : "unknown",
+ ast_bridge_after_cb_reason_string(reason));
+
+ if (reason == AST_BRIDGE_AFTER_CB_REASON_IMPART_FAILED) {
+ /* The impart actually failed so control->bridge isn't valid. */
+ control->bridge = NULL;
+ }
ast_assert(chan == control->channel);
@@ -942,18 +949,21 @@ static void bridge_after_cb(struct ast_channel *chan, void *data)
ast_channel_pbx_set(control->channel, control->pbx);
control->pbx = NULL;
- app_unsubscribe_bridge(control->app, control->bridge);
+ if (control->bridge) {
+ app_unsubscribe_bridge(control->app, control->bridge);
- /* No longer in the bridge */
- control->bridge = NULL;
+ /* No longer in the bridge */
+ control->bridge = NULL;
- /* Get the bridge channel so we don't depart from the wrong bridge */
- ast_channel_lock(chan);
- bridge_channel = ast_channel_get_bridge_channel(chan);
- ast_channel_unlock(chan);
+ /* Get the bridge channel so we don't depart from the wrong bridge */
+ ast_channel_lock(chan);
+ bridge_channel = ast_channel_get_bridge_channel(chan);
+ ast_channel_unlock(chan);
+
+ /* Depart this channel from the bridge using the command queue if possible */
+ stasis_app_send_command_async(control, bridge_channel_depart, bridge_channel, __ao2_cleanup);
+ }
- /* Depart this channel from the bridge using the command queue if possible */
- stasis_app_send_command_async(control, bridge_channel_depart, bridge_channel, __ao2_cleanup);
if (stasis_app_channel_is_stasis_end_published(chan)) {
/* The channel has had a StasisEnd published on it, but until now had remained in
* the bridging system. This means that the channel moved from a Stasis bridge to a
@@ -971,12 +981,19 @@ static void bridge_after_cb(struct ast_channel *chan, void *data)
}
}
+static void bridge_after_cb(struct ast_channel *chan, void *data)
+{
+ struct stasis_app_control *control = data;
+
+ internal_bridge_after_cb(control->channel, data, AST_BRIDGE_AFTER_CB_REASON_DEPART);
+}
+
static void bridge_after_cb_failed(enum ast_bridge_after_cb_reason reason,
void *data)
{
struct stasis_app_control *control = data;
- bridge_after_cb(control->channel, data);
+ internal_bridge_after_cb(control->channel, data, reason);
ast_debug(3, " reason: %s\n",
ast_bridge_after_cb_reason_string(reason));
@@ -1014,42 +1031,53 @@ int control_swap_channel_in_bridge(struct stasis_app_control *control, struct as
return -1;
}
- {
- /* pbx and bridge are modified by the bridging impart thread.
- * It shouldn't happen concurrently, but we still need to lock
- * for the memory fence.
- */
- SCOPED_AO2LOCK(lock, control);
+ ao2_lock(control);
- /* Ensure the controlling application is subscribed early enough
- * to receive the ChannelEnteredBridge message. This works in concert
- * with the subscription handled in the Stasis application execution
- * loop */
- app_subscribe_bridge(control->app, bridge);
-
- /* Save off the channel's PBX */
- ast_assert(control->pbx == NULL);
- if (!control->pbx) {
- control->pbx = ast_channel_pbx(chan);
- ast_channel_pbx_set(chan, NULL);
- }
+ /* Ensure the controlling application is subscribed early enough
+ * to receive the ChannelEnteredBridge message. This works in concert
+ * with the subscription handled in the Stasis application execution
+ * loop */
+ app_subscribe_bridge(control->app, bridge);
- res = ast_bridge_impart(bridge,
- chan,
- swap,
- NULL, /* features */
- AST_BRIDGE_IMPART_CHAN_DEPARTABLE);
- if (res != 0) {
- ast_log(LOG_ERROR, "Error adding channel to bridge\n");
- ast_channel_pbx_set(chan, control->pbx);
- control->pbx = NULL;
- return -1;
- }
+ /* Save off the channel's PBX */
+ ast_assert(control->pbx == NULL);
+ if (!control->pbx) {
+ control->pbx = ast_channel_pbx(chan);
+ ast_channel_pbx_set(chan, NULL);
+ }
- ast_assert(stasis_app_get_bridge(control) == NULL);
- control->bridge = bridge;
+ ast_assert(stasis_app_get_bridge(control) == NULL);
+ /* We need to set control->bridge here since bridge_after_cb may be run
+ * before ast_bridge_impart returns. bridge_after_cb gets a reason
+ * code so it can tell if the bridge is actually valid or not.
+ */
+ control->bridge = bridge;
+
+ /* We can't be holding the control lock while impart is running
+ * or we could create a deadlock with bridge_after_cb which also
+ * tries to lock control.
+ */
+ ao2_unlock(control);
+ res = ast_bridge_impart(bridge,
+ chan,
+ swap,
+ NULL, /* features */
+ AST_BRIDGE_IMPART_CHAN_DEPARTABLE);
+ if (res != 0) {
+ /* ast_bridge_impart failed before it could spawn the depart
+ * thread. The callbacks aren't called in this case.
+ * The impart could still fail even if ast_bridge_impart returned
+ * ok but that's handled by bridge_after_cb.
+ */
+ ast_log(LOG_ERROR, "Error adding channel to bridge\n");
+ ao2_lock(control);
+ ast_channel_pbx_set(chan, control->pbx);
+ control->pbx = NULL;
+ control->bridge = NULL;
+ ao2_unlock(control);
}
- return 0;
+
+ return res;
}
int control_add_channel_to_bridge(struct stasis_app_control *control, struct ast_channel *chan, void *data)