diff options
53 files changed, 1606 insertions, 1549 deletions
@@ -30,6 +30,15 @@ chan_sip --- Functionality changes from Asterisk 15.1.0 to Asterisk 15.2.0 ------------ ------------------------------------------------------------------------------ +Core +------------------ + * Added the "cache_media_frames" option to asterisk.conf. Disabling the option + helps track down media frame mismanagement when using valgrind or + MALLOC_DEBUG. The cache gets in the way of determining if the frame is + used after free and who freed it. NOTE: This option has no effect when + Asterisk is compiled with the LOW_MEMORY compile time option enabled because + the cache code does not exist. + res_rtp_asterisk ------------------ * The X.509 certificate used for DTLS negotation can now be automatically @@ -1111,3 +1111,6 @@ check-alembic: makeopts FORCE: +# This only stops targets within the root Makefile from building in parallel. +.NOTPARALLEL: + diff --git a/apps/app_record.c b/apps/app_record.c index 8c3a577ef..b9014fed7 100644 --- a/apps/app_record.c +++ b/apps/app_record.c @@ -137,6 +137,12 @@ enum { OPTION_NO_TRUNCATE = (1 << 9), }; +enum dtmf_response { + RESPONSE_NO_MATCH = 0, + RESPONSE_OPERATOR, + RESPONSE_DTMF, +}; + AST_APP_OPTIONS(app_opts,{ AST_APP_OPTION('a', OPTION_APPEND), AST_APP_OPTION('k', OPTION_KEEP), @@ -160,24 +166,22 @@ AST_APP_OPTIONS(app_opts,{ * \param dtmf_integer the integer value of the DTMF key received * \param terminator key currently set to be pressed for normal termination * - * \retval 0 do not exit - * \retval -1 do exit + * \returns One of enum dtmf_response */ -static int record_dtmf_response(struct ast_channel *chan, struct ast_flags *flags, int dtmf_integer, int terminator) +static enum dtmf_response record_dtmf_response(struct ast_channel *chan, + struct ast_flags *flags, int dtmf_integer, int terminator) { if ((dtmf_integer == OPERATOR_KEY) && (ast_test_flag(flags, OPTION_OPERATOR_EXIT))) { - pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "OPERATOR"); - return -1; + return RESPONSE_OPERATOR; } if ((dtmf_integer == terminator) || (ast_test_flag(flags, OPTION_ANY_TERMINATE))) { - pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "DTMF"); - return -1; + return RESPONSE_DTMF; } - return 0; + return RESPONSE_NO_MATCH; } static int create_destination_directory(const char *path) @@ -246,6 +250,7 @@ static int record_exec(struct ast_channel *chan, const char *data) ); int ms; struct timeval start; + const char *status_response = "ERROR"; /* The next few lines of code parse out the filename and header from the input string */ if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */ @@ -343,7 +348,7 @@ static int record_exec(struct ast_channel *chan, const char *data) if (res) { ast_log(LOG_WARNING, "Could not answer channel '%s'\n", ast_channel_name(chan)); - pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR"); + status_response = "ERROR"; goto out; } @@ -379,7 +384,7 @@ static int record_exec(struct ast_channel *chan, const char *data) if (create_destination_directory(tmp)) { ast_log(LOG_WARNING, "Could not create directory for file %s\n", args.filename); - pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR"); + status_response = "ERROR"; goto out; } @@ -388,7 +393,7 @@ static int record_exec(struct ast_channel *chan, const char *data) if (!s) { ast_log(LOG_WARNING, "Could not create file %s\n", args.filename); - pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR"); + status_response = "ERROR"; goto out; } @@ -423,7 +428,7 @@ static int record_exec(struct ast_channel *chan, const char *data) if (res) { ast_log(LOG_WARNING, "Problem writing frame\n"); ast_frfree(f); - pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR"); + status_response = "ERROR"; break; } @@ -439,7 +444,7 @@ static int record_exec(struct ast_channel *chan, const char *data) /* Ended happily with silence */ ast_frfree(f); gotsilence = 1; - pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "SILENCE"); + status_response = "SILENCE"; break; } } @@ -448,12 +453,26 @@ static int record_exec(struct ast_channel *chan, const char *data) if (res) { ast_log(LOG_WARNING, "Problem writing frame\n"); - pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR"); + status_response = "ERROR"; ast_frfree(f); break; } } else if (f->frametype == AST_FRAME_DTMF) { - if (record_dtmf_response(chan, &flags, f->subclass.integer, terminator)) { + enum dtmf_response rc = + record_dtmf_response(chan, &flags, f->subclass.integer, terminator); + switch(rc) { + case RESPONSE_NO_MATCH: + break; + case RESPONSE_OPERATOR: + status_response = "OPERATOR"; + ast_debug(1, "Got OPERATOR\n"); + break; + case RESPONSE_DTMF: + status_response = "DTMF"; + ast_debug(1, "Got DTMF\n"); + break; + } + if (rc != RESPONSE_NO_MATCH) { ast_frfree(f); break; } @@ -463,13 +482,13 @@ static int record_exec(struct ast_channel *chan, const char *data) if (maxduration > 0 && !ms) { gottimeout = 1; - pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "TIMEOUT"); + status_response = "TIMEOUT"; } if (!f) { ast_debug(1, "Got hangup\n"); res = -1; - pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "HANGUP"); + status_response = "HANGUP"; if (!ast_test_flag(&flags, OPTION_KEEP)) { ast_filedelete(args.filename, NULL); } @@ -504,6 +523,9 @@ out: if (sildet) { ast_dsp_free(sildet); } + + pbx_builtin_setvar_helper(chan, "RECORD_STATUS", status_response); + return res; } diff --git a/autoconf/ast_gcc_attribute.m4 b/autoconf/ast_gcc_attribute.m4 index 4ade81404..b1972bedf 100644 --- a/autoconf/ast_gcc_attribute.m4 +++ b/autoconf/ast_gcc_attribute.m4 @@ -7,6 +7,7 @@ AC_MSG_CHECKING(for compiler 'attribute $1' support) saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" m4_ifval([$4],$4=0) +ax_cv_have_func_attribute_$1=0 if test "x$2" = "x" then @@ -15,6 +16,7 @@ AC_COMPILE_IFELSE( [])], AC_MSG_RESULT(yes) m4_ifval([$4],$4=1) + ax_cv_have_func_attribute_$1=1 AC_DEFINE_UNQUOTED([HAVE_ATTRIBUTE_$1], 1, [Define to 1 if your GCC C compiler supports the '$1' attribute.]), AC_MSG_RESULT(no) ) @@ -24,6 +26,7 @@ AC_COMPILE_IFELSE( [])], AC_MSG_RESULT(yes) m4_ifval([$4],$4=1) + ax_cv_have_func_attribute_$1=1 AC_DEFINE_UNQUOTED([HAVE_ATTRIBUTE_$1], 1, [Define to 1 if your GCC C compiler supports the '$1' attribute.]), AC_MSG_RESULT(no) ) diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c index f490967e2..8de88f257 100644 --- a/bridges/bridge_softmix.c +++ b/bridges/bridge_softmix.c @@ -541,6 +541,8 @@ static int append_all_streams(struct ast_stream_topology *dest, dest_index++; if (ast_stream_get_state(stream) == AST_STREAM_STATE_REMOVED) { + /* This cannot fail because dest_index - 1 is less than the + * current count in dest. */ ast_stream_topology_set_stream(dest, dest_index - 1, clone); added = 1; break; diff --git a/build_tools/menuselect-deps.in b/build_tools/menuselect-deps.in index 9becdb62e..259a8358e 100644 --- a/build_tools/menuselect-deps.in +++ b/build_tools/menuselect-deps.in @@ -1,5 +1,6 @@ ALSA=@PBX_ALSA@ BLUETOOTH=@PBX_BLUETOOTH@ +BEANSTALK=@PBX_BEANSTALK@ COROSYNC=@PBX_COROSYNC@ CRYPTO=@PBX_CRYPTO@ BFD=@PBX_BFD@ diff --git a/cdr/cdr_beanstalkd.c b/cdr/cdr_beanstalkd.c new file mode 100644 index 000000000..4812c91aa --- /dev/null +++ b/cdr/cdr_beanstalkd.c @@ -0,0 +1,270 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2017 + * + * Nir Simionovich <nirs@greenfieldtech.net> + * + * 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 Asterisk Beanstalkd CDR records. + * + * This module requires the beanstalk-client library, avaialble from + * https://github.com/deepfryed/beanstalk-client + * + * See also + * \arg \ref AstCDR + * \ingroup cdr_drivers + */ + +/*! \li \ref cdr_beanstalkd.c uses the configuration file \ref cdr_beanstalkd.conf + * \addtogroup configuration_file Configuration Files + */ + +/*! + * \page cdr_beanstalkd.conf cdr_beanstalkd.conf + * \verbinclude cdr_beanstalkd.conf.sample + */ + +/*** MODULEINFO + <depend>beanstalk</depend> + <support_level>extended</support_level> + ***/ + +#include "asterisk.h" + +#include <time.h> +#include <stdio.h> + +#include "beanstalk.h" +#include "asterisk/channel.h" +#include "asterisk/cdr.h" +#include "asterisk/module.h" +#include "asterisk/utils.h" +#include "asterisk/manager.h" +#include "asterisk/config.h" +#include "asterisk/pbx.h" +#include "asterisk/json.h" + +#define DATE_FORMAT "%Y-%m-%d %T" +#define CONF_FILE "cdr_beanstalkd.conf" +#define BEANSTALK_JOB_SIZE 4096 +#define BEANSTALK_JOB_PRIORITY 99 +#define BEANSTALK_JOB_TTR 60 +#define BEANSTALK_JOB_DELAY 0 +#define DEFAULT_BEANSTALK_HOST "127.0.0.1" +#define DEFAULT_BEANSTALK_PORT 11300 +#define DEFAULT_BEANSTALK_TUBE "asterisk-cdr" + +static const char name[] = "cdr_beanstalkd"; + +static int enablecdr = 0; +static char *bs_host; +static int bs_port; +static char *bs_tube; +static int priority; + +AST_RWLOCK_DEFINE_STATIC(config_lock); + +static int beanstalk_put(struct ast_cdr *cdr); + +static int load_config(int reload) { + char *cat = NULL; + struct ast_config *cfg; + struct ast_variable *v; + struct ast_flags config_flags = {reload ? CONFIG_FLAG_FILEUNCHANGED : 0}; + int newenablecdr = 0; + + cfg = ast_config_load(CONF_FILE, config_flags); + if (cfg == CONFIG_STATUS_FILEUNCHANGED) { + return 0; + } + + if (cfg == CONFIG_STATUS_FILEINVALID) { + ast_log(LOG_ERROR, "Config file '%s' could not be parsed\n", CONF_FILE); + return -1; + } + + if (!cfg) { + /* Standard configuration */ + ast_log(LOG_WARNING, "Failed to load configuration file. Module not activated.\n"); + if (enablecdr) { + ast_cdr_backend_suspend(name); + } + enablecdr = 0; + return -1; + } + + if (reload) { + ast_rwlock_wrlock(&config_lock); + ast_free(bs_host); + ast_free(bs_tube); + } + + /* Bootstrap the default configuration */ + bs_host = ast_strdup(DEFAULT_BEANSTALK_HOST); + bs_port = DEFAULT_BEANSTALK_PORT; + bs_tube = ast_strdup(DEFAULT_BEANSTALK_TUBE); + priority = BEANSTALK_JOB_PRIORITY; + + while ((cat = ast_category_browse(cfg, cat))) { + if (!strcasecmp(cat, "general")) { + v = ast_variable_browse(cfg, cat); + while (v) { + + if (!strcasecmp(v->name, "enabled")) { + newenablecdr = ast_true(v->value); + } else if (!strcasecmp(v->name, "host")) { + ast_free(bs_host); + bs_host = ast_strdup(v->value); + } else if (!strcasecmp(v->name, "port")) { + bs_port = atoi(v->value); + } else if (!strcasecmp(v->name, "tube")) { + ast_free(bs_tube); + bs_tube = ast_strdup(v->value); + } else if (!strcasecmp(v->name, "priority")) { + priority = atoi(v->value); + } + v = v->next; + + } + } + } + + if (reload) { + ast_rwlock_unlock(&config_lock); + } + + ast_config_destroy(cfg); + + if (!newenablecdr) { + ast_cdr_backend_suspend(name); + } else if (newenablecdr) { + ast_cdr_backend_unsuspend(name); + ast_log(LOG_NOTICE, "Added beanstalkd server %s at port %d with tube %s", bs_host, bs_port, bs_tube); + } + enablecdr = newenablecdr; + + return 0; +} + +static int beanstalk_put(struct ast_cdr *cdr) { + struct ast_tm timeresult; + char strAnswerTime[80] = ""; + char strStartTime[80]; + char strEndTime[80]; + char *cdr_buffer; + int bs_id; + int bs_socket; + struct ast_json *t_cdr_json; + + if (!enablecdr) { + return 0; + } + + ast_rwlock_rdlock(&config_lock); + bs_socket = bs_connect(bs_host, bs_port); + + if (bs_use(bs_socket, bs_tube) != BS_STATUS_OK) { + ast_log(LOG_ERROR, "Connection to Beanstalk tube %s @ %s:%d had failed", bs_tube, bs_host, bs_port); + ast_rwlock_unlock(&config_lock); + return 0; + } + + ast_localtime(&cdr->start, &timeresult, NULL); + ast_strftime(strStartTime, sizeof(strStartTime), DATE_FORMAT, &timeresult); + + if (cdr->answer.tv_sec) { + ast_localtime(&cdr->answer, &timeresult, NULL); + ast_strftime(strAnswerTime, sizeof(strAnswerTime), DATE_FORMAT, &timeresult); + } + + ast_localtime(&cdr->end, &timeresult, NULL); + ast_strftime(strEndTime, sizeof(strEndTime), DATE_FORMAT, &timeresult); + + ast_rwlock_unlock(&config_lock); + + t_cdr_json = ast_json_pack("{s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:i, s:i, s:s, s:s, s:s, s:s}", + "AccountCode", S_OR(cdr->accountcode, ""), + "Source", S_OR(cdr->src, ""), + "Destination", S_OR(cdr->dst, ""), + "DestinationContext", S_OR(cdr->dcontext, ""), + "CallerID", S_OR(cdr->clid, ""), + "Channel", S_OR(cdr->channel, ""), + "DestinationChannel", S_OR(cdr->dstchannel, ""), + "LastApplication", S_OR(cdr->lastapp, ""), + "LastData", S_OR(cdr->lastdata, ""), + "StartTime", S_OR(strStartTime, ""), + "AnswerTime", S_OR(strAnswerTime, ""), + "EndTime", S_OR(strEndTime, ""), + "Duration", cdr->duration, + "Billsec", cdr->billsec, + "Disposition", S_OR(ast_cdr_disp2str(cdr->disposition), ""), + "AMAFlags", S_OR(ast_channel_amaflags2string(cdr->amaflags), ""), + "UniqueID", S_OR(cdr->uniqueid, ""), + "UserField", S_OR(cdr->userfield, "")); + + cdr_buffer = ast_json_dump_string(t_cdr_json); + + ast_json_unref(t_cdr_json); + + bs_id = bs_put(bs_socket, priority, BEANSTALK_JOB_DELAY, BEANSTALK_JOB_TTR, cdr_buffer, strlen(cdr_buffer)); + + if (bs_id > 0) { + ast_log(LOG_DEBUG, "Successfully created job %d with %s\n", bs_id, cdr_buffer); + } else { + ast_log(LOG_ERROR, "CDR job creation failed for %s\n", cdr_buffer); + } + + bs_disconnect(bs_socket); + ast_json_free(cdr_buffer); + return 0; +} + +static int unload_module(void) { + if (ast_cdr_unregister(name)) { + return -1; + } + + ast_free(bs_host); + ast_free(bs_tube); + + return 0; +} + +static int load_module(void) { + if (ast_cdr_register(name, "Asterisk CDR Beanstalkd Backend", beanstalk_put)) { + return AST_MODULE_LOAD_DECLINE; + } + + if (load_config(0)) { + ast_cdr_unregister(name); + return AST_MODULE_LOAD_DECLINE; + } + + return AST_MODULE_LOAD_SUCCESS; +} + +static int reload(void) { + return load_config(1); +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Asterisk Beanstalkd CDR Backend", +.support_level = AST_MODULE_SUPPORT_EXTENDED, +.load = load_module, +.unload = unload_module, +.reload = reload, +.load_pri = AST_MODPRI_CDR_DRIVER, +); + diff --git a/cel/cel_beanstalkd.c b/cel/cel_beanstalkd.c new file mode 100644 index 000000000..e6f23808c --- /dev/null +++ b/cel/cel_beanstalkd.c @@ -0,0 +1,275 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2017, Greenfield Technologies Ltd. + * + * Nir Simionovich <nirs@greenfieldtech.net> + * who freely borrowed code from the cel manager equivalents + * (see cel/cel_manager.c) + * + * 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 Asterisk Channel Event Beanstalkd backend + * + * This module requires the beanstalk-client library, avaialble from + * https://github.com/deepfryed/beanstalk-client + * + * See also + * \arg \ref AstCEL + * \ingroup cel_drivers + */ + +/*! \li \ref cek_beanstalkd.c uses the configuration file \ref cel.conf + * \addtogroup configuration_file Configuration Files + */ + +/*! + * \page cel.conf cel.conf + * \verbinclude cel.conf.sample + */ + +/*** MODULEINFO + <depend>beanstalk</depend> + <support_level>extended</support_level> + ***/ + +#include "asterisk.h" + +#include "asterisk/channel.h" +#include "asterisk/cel.h" +#include "asterisk/module.h" +#include "asterisk/logger.h" +#include "asterisk/utils.h" +#include "asterisk/manager.h" +#include "asterisk/config.h" +#include "asterisk/json.h" + +#include "beanstalk.h" + +static const char DATE_FORMAT[] = "%Y-%m-%d %T"; + +static const char CONF_FILE[] = "cel_beanstalkd.conf"; + +/*! \brief Beanstalk CEL is off by default */ +#define CEL_BEANSTALK_ENABLED_DEFAULT 0 + +static int enablecel; + +/*! \brief show_user_def is off by default */ +#define CEL_SHOW_USERDEF_DEFAULT 0 + +#define CEL_BACKEND_NAME "Beanstalk Event Logging" + +#define BEANSTALK_JOB_SIZE 4096 +#define BEANSTALK_JOB_PRIORITY 99 +#define BEANSTALK_JOB_TTR 60 +#define BEANSTALK_JOB_DELAY 0 +#define DEFAULT_BEANSTALK_HOST "127.0.0.1" +#define DEFAULT_BEANSTALK_PORT 11300 +#define DEFAULT_BEANSTALK_TUBE "asterisk-cel" + +static char *bs_host; +static int bs_port; +static char *bs_tube; +static int priority; + +AST_RWLOCK_DEFINE_STATIC(config_lock); + +static void cel_bs_put(struct ast_event *event) +{ + struct ast_tm timeresult; + char start_time[80]; + char *cel_buffer; + int bs_id; + int bs_socket; + struct ast_json *t_cel_json; + + struct ast_cel_event_record record = { + .version = AST_CEL_EVENT_RECORD_VERSION, + }; + + if (!enablecel) { + return; + } + + if (ast_cel_fill_record(event, &record)) { + return; + } + + ast_rwlock_rdlock(&config_lock); + bs_socket = bs_connect(bs_host, bs_port); + + if (bs_use(bs_socket, bs_tube) != BS_STATUS_OK) { + ast_log(LOG_ERROR, "Connection to Beanstalk tube %s @ %s:%d had failed", bs_tube, bs_host, bs_port); + ast_rwlock_unlock(&config_lock); + return; + } + + ast_localtime(&record.event_time, &timeresult, NULL); + ast_strftime(start_time, sizeof(start_time), DATE_FORMAT, &timeresult); + + ast_rwlock_unlock(&config_lock); + + t_cel_json = ast_json_pack("{s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s}", + "EventName", S_OR(record.event_name, ""), + "AccountCode", S_OR(record.account_code, ""), + "CallerIDnum", S_OR(record.caller_id_num, ""), + "CallerIDname", S_OR(record.caller_id_name, ""), + "CallerIDani", S_OR(record.caller_id_ani, ""), + "CallerIDrdnis", S_OR(record.caller_id_rdnis, ""), + "CallerIDdnid", S_OR(record.caller_id_dnid, ""), + "Exten", S_OR(record.extension, ""), + "Context", S_OR(record.context, ""), + "Channel", S_OR(record.channel_name, ""), + "Application", S_OR(record.application_name, ""), + "AppData", S_OR(record.application_data, ""), + "EventTime", S_OR(start_time, ""), + "AMAFlags", S_OR(ast_channel_amaflags2string(record.amaflag), ""), + "UniqueID", S_OR(record.unique_id, ""), + "LinkedID", S_OR(record.linked_id, ""), + "Userfield", S_OR(record.user_field, ""), + "Peer", S_OR(record.peer_account, ""), + "PeerAccount", S_OR(record.peer_account, ""), + "Extra", S_OR(record.extra, "") + + ); + + cel_buffer = ast_json_dump_string(t_cel_json); + + ast_json_unref(t_cel_json); + + bs_id = bs_put(bs_socket, priority, BEANSTALK_JOB_DELAY, BEANSTALK_JOB_TTR, cel_buffer, strlen(cel_buffer)); + + if (bs_id > 0) { + ast_log(LOG_DEBUG, "Successfully created job %d with %s\n", bs_id, cel_buffer); + } else { + ast_log(LOG_ERROR, "CDR job creation failed for %s\n", cel_buffer); + } + + bs_disconnect(bs_socket); + ast_json_free(cel_buffer); +} + +static int load_config(int reload) +{ + const char *cat = NULL; + struct ast_config *cfg; + struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; + struct ast_variable *v; + int newenablecel = CEL_BEANSTALK_ENABLED_DEFAULT; + + cfg = ast_config_load(CONF_FILE, config_flags); + if (cfg == CONFIG_STATUS_FILEUNCHANGED) { + return 0; + } + + if (cfg == CONFIG_STATUS_FILEINVALID) { + ast_log(LOG_WARNING, "Configuration file '%s' is invalid. CEL Beanstalkd Module not activated.\n", + CONF_FILE); + return -1; + } else if (!cfg) { + ast_log(LOG_WARNING, "Failed to load configuration file. CEL Beanstalkd Module not activated.\n"); + if (enablecel) { + ast_cel_backend_unregister(CEL_BACKEND_NAME); + } + enablecel = 0; + return -1; + } + + if (reload) { + ast_rwlock_wrlock(&config_lock); + ast_free(bs_host); + ast_free(bs_tube); + } + + /* Bootstrap the default configuration */ + bs_host = ast_strdup(DEFAULT_BEANSTALK_HOST); + bs_port = DEFAULT_BEANSTALK_PORT; + bs_tube = ast_strdup(DEFAULT_BEANSTALK_TUBE); + priority = BEANSTALK_JOB_PRIORITY; + + while ((cat = ast_category_browse(cfg, cat))) { + + if (strcasecmp(cat, "general")) { + continue; + } + + for (v = ast_variable_browse(cfg, cat); v; v = v->next) { + if (!strcasecmp(v->name, "enabled")) { + newenablecel = ast_true(v->value) ? 1 : 0; + } else if (!strcasecmp(v->name, "host")) { + ast_free(bs_host); + bs_host = ast_strdup(v->value); + } else if (!strcasecmp(v->name, "port")) { + bs_port = atoi(v->value); + } else if (!strcasecmp(v->name, "tube")) { + ast_free(bs_tube); + bs_tube = ast_strdup(v->value); + } else if (!strcasecmp(v->name, "priority")) { + priority = atoi(v->value); + } else { + ast_log(LOG_NOTICE, "Unknown option '%s' specified " + "for CEL beanstalk backend.\n", v->name); + } + } + } + + if (reload) { + ast_rwlock_unlock(&config_lock); + } + + ast_config_destroy(cfg); + + if (enablecel && !newenablecel) { + ast_cel_backend_unregister(CEL_BACKEND_NAME); + } else if (!enablecel && newenablecel) { + if (ast_cel_backend_register(CEL_BACKEND_NAME, cel_bs_put)) { + ast_log(LOG_ERROR, "Unable to register Beanstalkd CEL handling\n"); + } + } + + enablecel = newenablecel; + + return 0; +} + +static int unload_module(void) +{ + ast_cel_backend_unregister(CEL_BACKEND_NAME); + ast_free(bs_host); + ast_free(bs_tube); + return 0; +} + +static int load_module(void) +{ + if (load_config(0)) { + return AST_MODULE_LOAD_DECLINE; + } + + return AST_MODULE_LOAD_SUCCESS; +} + +static int reload(void) +{ + return load_config(1); +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Beanstalkd CEL Backend", + .support_level = AST_MODULE_SUPPORT_EXTENDED, + .load = load_module, + .unload = unload_module, + .reload = reload, + .load_pri = AST_MODPRI_CDR_DRIVER, +); diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c index 7520c2b0e..e4e8fa586 100644 --- a/channels/chan_pjsip.c +++ b/channels/chan_pjsip.c @@ -672,7 +672,19 @@ static int answer(void *data) pjsip_inv_dec_ref(session->inv_session); #endif - return (status == PJ_SUCCESS) ? 0 : -1; + if (status != PJ_SUCCESS) { + char err[PJ_ERR_MSG_SIZE]; + + pj_strerror(status, err, sizeof(err)); + ast_log(LOG_WARNING,"Cannot answer '%s': %s\n", + ast_channel_name(session->channel), err); + /* + * Return this value so we can distinguish between this + * failure and the threadpool synchronous push failing. + */ + return -2; + } + return 0; } /*! \brief Function called by core when we should answer a PJSIP session */ @@ -680,6 +692,7 @@ static int chan_pjsip_answer(struct ast_channel *ast) { struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast); struct ast_sip_session *session; + int res; if (ast_channel_state(ast) == AST_STATE_UP) { return 0; @@ -700,11 +713,15 @@ static int chan_pjsip_answer(struct ast_channel *ast) can occur between this thread and bridging (specifically when native bridging attempts to do direct media) */ ast_channel_unlock(ast); - if (ast_sip_push_task_synchronous(session->serializer, answer, session)) { - ast_log(LOG_WARNING, "Unable to push answer task to the threadpool. Cannot answer call\n"); + res = ast_sip_push_task_synchronous(session->serializer, answer, session); + if (res) { + if (res == -1) { + ast_log(LOG_ERROR,"Cannot answer '%s': Unable to push answer task to the threadpool.\n", + ast_channel_name(session->channel)); #ifdef HAVE_PJSIP_INV_SESSION_REF - pjsip_inv_dec_ref(session->inv_session); + pjsip_inv_dec_ref(session->inv_session); #endif + } ao2_ref(session, -1); ast_channel_lock(ast); return -1; @@ -2442,6 +2459,8 @@ static struct ast_channel *chan_pjsip_request_with_stream_topology(const char *t req_data.topology = topology; req_data.dest = data; + /* Default failure value in case ast_sip_push_task_synchronous() itself fails. */ + req_data.cause = AST_CAUSE_FAILURE; if (ast_sip_push_task_synchronous(NULL, request, &req_data)) { *cause = req_data.cause; diff --git a/channels/iax2/parser.c b/channels/iax2/parser.c index ec9d34608..6eda98260 100644 --- a/channels/iax2/parser.c +++ b/channels/iax2/parser.c @@ -1296,7 +1296,9 @@ void iax_frame_free(struct iax_frame *fr) ast_atomic_fetchadd_int(&frames, -1); #if !defined(LOW_MEMORY) - if (!fr->cacheable || !(iax_frames = ast_threadstorage_get(&frame_cache, sizeof(*iax_frames)))) { + if (!fr->cacheable + || !ast_opt_cache_media_frames + || !(iax_frames = ast_threadstorage_get(&frame_cache, sizeof(*iax_frames)))) { ast_free(fr); return; } diff --git a/configs/samples/ast_debug_tools.conf.sample b/configs/samples/ast_debug_tools.conf.sample index f26626b20..1c4827f91 100644 --- a/configs/samples/ast_debug_tools.conf.sample +++ b/configs/samples/ast_debug_tools.conf.sample @@ -20,6 +20,12 @@ # anyway. COREDUMPS=(/tmp/core[-._]asterisk!(*.txt) /tmp/core[-._]$(hostname)!(*.txt)) +# The directory to contain output files and work directories. +# For output from existing core files, the default is the +# directory that the core file is found in. For core files +# produced from a running process, the default is /tmp. +OUTPUTDIR=/some/directory + # Date command for the "running" coredump and tarballs. # DATEFORMAT will be executed to get the timestamp. # Don't put quotes around the format string or they'll be diff --git a/configs/samples/asterisk.conf.sample b/configs/samples/asterisk.conf.sample index 30934e4a9..8379b6ef0 100644 --- a/configs/samples/asterisk.conf.sample +++ b/configs/samples/asterisk.conf.sample @@ -44,6 +44,15 @@ astsbindir => /usr/sbin ;minmemfree = 1 ; In MBs, Asterisk stops accepting new calls if ; the amount of free memory falls below this ; watermark. +;cache_media_frames = yes ; Cache media frames for performance + ; Disable this option to help track down media frame + ; mismanagement when using valgrind or MALLOC_DEBUG. + ; The cache gets in the way of determining if the + ; frame is used after being freed and who freed it. + ; NOTE: This option has no effect when Asterisk is + ; compiled with the LOW_MEMORY compile time option + ; enabled because the cache code does not exist. + ; Default yes ;cache_record_files = yes ; Cache recorded sound files to another ; directory during recording. ;record_cache_dir = /tmp ; Specify cache directory (used in conjunction diff --git a/configs/samples/cdr_beanstalkd.conf.sample b/configs/samples/cdr_beanstalkd.conf.sample new file mode 100644 index 000000000..5f45d5d5d --- /dev/null +++ b/configs/samples/cdr_beanstalkd.conf.sample @@ -0,0 +1,26 @@ +; +; Asterisk Call Management CDR via Beanstalkd job queue +; +; Beanstalkd is a simple job queue server, that is highly versatile and simple to use. +; Beanstalkd includes the capability of using multiple queues at the same time, with priorities. +; +; This module requires that your server has the beanstalk-client library installed. The library +; can be downloaded from - https://github.com/deepfryed/beanstalk-client +; + +[general] +;enabled = yes + +;host = 127.0.0.1 ; Specify the remote IP address of the Beanstalkd server +;port = 11300 ; Specify the remote PORT of the the Beanstalkd server +;tube = asterisk-cdr ; Specify the default CDR job queue to use +;priority = 99 ; Specify the default job priority for the queue. This parameter is useful when building + ; platform with multiple Asterisk servers, that are used for different functions. For example, + ; none billable CDR records can be inserted with a priority of 99, while billable ones be + ; inserted with a priority of 1 + + + + + + diff --git a/configs/samples/cel_beanstalkd.conf.sample b/configs/samples/cel_beanstalkd.conf.sample new file mode 100644 index 000000000..8cd0134cc --- /dev/null +++ b/configs/samples/cel_beanstalkd.conf.sample @@ -0,0 +1,21 @@ +; +; Beanstalkd Job Queue Server CEL Backend +; +[general] + +; Backend Activation +; +; Use the 'enabled' keyword to turn CEL logging +; on or off. +; +; Accepted values: yes and no +; Default value: no +;enabled = yes + +;host = 127.0.0.1 ; Specify the remote IP address of the Beanstalkd server +;port = 11300 ; Specify the remote PORT of the the Beanstalkd server +;tube = asterisk-cel ; Specify the default CDR job queue to use +;priority = 99 ; Specify the default job priority for the queue. This parameter is useful when building + ; platform with multiple Asterisk servers, that are used for different functions. For example, + ; none billable CDR records can be inserted with a priority of 99, while billable ones be + ; inserted with a priority of 1 @@ -996,6 +996,10 @@ PBX_PJSIP_DLG_CREATE_UAS_AND_INC_LOCK PJSIP_DLG_CREATE_UAS_AND_INC_LOCK_DIR PJSIP_DLG_CREATE_UAS_AND_INC_LOCK_INCLUDE PJSIP_DLG_CREATE_UAS_AND_INC_LOCK_LIB +PBX_BEANSTALK +BEANSTALK_DIR +BEANSTALK_INCLUDE +BEANSTALK_LIB PBX_PGSQL PGSQL_DIR PGSQL_INCLUDE @@ -1443,6 +1447,7 @@ with_opusfile with_osptk with_oss with_postgres +with_beanstalk with_pjproject with_popt with_portaudio @@ -2200,6 +2205,7 @@ Optional Packages: --with-osptk=PATH use OSP Toolkit files in PATH --with-oss=PATH use Open Sound System files in PATH --with-postgres=PATH use PostgreSQL files in PATH + --with-beanstalk=PATH use Beanstalk Job Queue files in PATH --with-pjproject=PATH use PJPROJECT files in PATH --with-popt=PATH use popt files in PATH --with-portaudio=PATH use PortAudio files in PATH @@ -11491,6 +11497,38 @@ fi + BEANSTALK_DESCRIP="Beanstalk Job Queue" + BEANSTALK_OPTION="beanstalk" + PBX_BEANSTALK=0 + +# Check whether --with-beanstalk was given. +if test "${with_beanstalk+set}" = set; then : + withval=$with_beanstalk; + case ${withval} in + n|no) + USE_BEANSTALK=no + # -1 is a magic value used by menuselect to know that the package + # was disabled, other than 'not found' + PBX_BEANSTALK=-1 + ;; + y|ye|yes) + ac_mandatory_list="${ac_mandatory_list} BEANSTALK" + ;; + *) + BEANSTALK_DIR="${withval}" + ac_mandatory_list="${ac_mandatory_list} BEANSTALK" + ;; + esac + +fi + + + + + + + + if test "x${PBX_PJPROJECT}" != "x1" ; then PJPROJECT_DESCRIP="PJPROJECT" @@ -18139,6 +18177,7 @@ $as_echo_n "checking for compiler 'attribute pure' support... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" +ax_cv_have_func_attribute_pure=0 if test "x" = "x" then @@ -18157,6 +18196,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_pure=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_pure 1 @@ -18184,6 +18224,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_pure=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_pure 1 @@ -18207,6 +18248,7 @@ $as_echo_n "checking for compiler 'attribute malloc' support... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" +ax_cv_have_func_attribute_malloc=0 if test "x" = "x" then @@ -18225,6 +18267,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_malloc=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_malloc 1 @@ -18252,6 +18295,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_malloc=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_malloc 1 @@ -18275,6 +18319,7 @@ $as_echo_n "checking for compiler 'attribute const' support... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" +ax_cv_have_func_attribute_const=0 if test "x" = "x" then @@ -18293,6 +18338,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_const=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_const 1 @@ -18320,6 +18366,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_const=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_const 1 @@ -18343,6 +18390,7 @@ $as_echo_n "checking for compiler 'attribute unused' support... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" +ax_cv_have_func_attribute_unused=0 if test "x" = "x" then @@ -18361,6 +18409,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_unused=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_unused 1 @@ -18388,6 +18437,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_unused=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_unused 1 @@ -18411,6 +18461,7 @@ $as_echo_n "checking for compiler 'attribute always_inline' support... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" +ax_cv_have_func_attribute_always_inline=0 if test "x" = "x" then @@ -18429,6 +18480,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_always_inline=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_always_inline 1 @@ -18456,6 +18508,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_always_inline=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_always_inline 1 @@ -18479,6 +18532,7 @@ $as_echo_n "checking for compiler 'attribute deprecated' support... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" +ax_cv_have_func_attribute_deprecated=0 if test "x" = "x" then @@ -18497,6 +18551,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_deprecated=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_deprecated 1 @@ -18524,6 +18579,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_deprecated=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_deprecated 1 @@ -18547,6 +18603,7 @@ $as_echo_n "checking for compiler 'attribute sentinel' support... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" +ax_cv_have_func_attribute_sentinel=0 if test "x" = "x" then @@ -18565,6 +18622,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_sentinel=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_sentinel 1 @@ -18592,6 +18650,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_sentinel=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_sentinel 1 @@ -18615,6 +18674,7 @@ $as_echo_n "checking for compiler 'attribute warn_unused_result' support... " >& saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" +ax_cv_have_func_attribute_warn_unused_result=0 if test "x" = "x" then @@ -18633,6 +18693,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_warn_unused_result=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_warn_unused_result 1 @@ -18660,6 +18721,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_warn_unused_result=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_warn_unused_result 1 @@ -18683,6 +18745,7 @@ $as_echo_n "checking for compiler 'attribute may_alias' support... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" +ax_cv_have_func_attribute_may_alias=0 if test "x" = "x" then @@ -18701,6 +18764,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_may_alias=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_may_alias 1 @@ -18728,6 +18792,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_may_alias=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_may_alias 1 @@ -18751,6 +18816,7 @@ $as_echo_n "checking for compiler 'attribute constructor' support... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" +ax_cv_have_func_attribute_constructor=0 if test "x" = "x" then @@ -18769,6 +18835,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_constructor=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_constructor 1 @@ -18796,6 +18863,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_constructor=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_constructor 1 @@ -18813,12 +18881,17 @@ fi CFLAGS="$saved_CFLAGS" +if test "$ax_cv_have_func_attribute_constructor" != "1"; then + as_fn_error $? "*** Function constructor attribute is not supported by your compiler." "$LINENO" 5 +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute destructor' support" >&5 $as_echo_n "checking for compiler 'attribute destructor' support... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" +ax_cv_have_func_attribute_destructor=0 if test "x" = "x" then @@ -18837,6 +18910,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_destructor=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_destructor 1 @@ -18864,6 +18938,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_destructor=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_destructor 1 @@ -18881,12 +18956,16 @@ fi CFLAGS="$saved_CFLAGS" +if test "$ax_cv_have_func_attribute_destructor" != "1"; then + as_fn_error $? "*** Function destructor attribute is not supported by your compiler." "$LINENO" 5 +fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute noreturn' support" >&5 $as_echo_n "checking for compiler 'attribute noreturn' support... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" +ax_cv_have_func_attribute_noreturn=0 if test "xnoreturn" = "x" then @@ -18905,6 +18984,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_noreturn=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_noreturn 1 @@ -18932,6 +19012,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + ax_cv_have_func_attribute_noreturn=1 cat >>confdefs.h <<_ACEOF #define HAVE_ATTRIBUTE_noreturn 1 @@ -25185,6 +25266,111 @@ fi + +if test "x${PBX_BEANSTALK}" != "x1" -a "${USE_BEANSTALK}" != "no"; then + pbxlibdir="" + # if --with-BEANSTALK=DIR has been specified, use it. + if test "x${BEANSTALK_DIR}" != "x"; then + if test -d ${BEANSTALK_DIR}/lib; then + pbxlibdir="-L${BEANSTALK_DIR}/lib" + else + pbxlibdir="-L${BEANSTALK_DIR}" + fi + fi + pbxfuncname="bs_version" + if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers + AST_BEANSTALK_FOUND=yes + else + ast_ext_lib_check_save_CFLAGS="${CFLAGS}" + CFLAGS="${CFLAGS} " + as_ac_Lib=`$as_echo "ac_cv_lib_beanstalk_${pbxfuncname}" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lbeanstalk" >&5 +$as_echo_n "checking for ${pbxfuncname} in -lbeanstalk... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lbeanstalk ${pbxlibdir} $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char ${pbxfuncname} (); +int +main () +{ +return ${pbxfuncname} (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + AST_BEANSTALK_FOUND=yes +else + AST_BEANSTALK_FOUND=no +fi + + CFLAGS="${ast_ext_lib_check_save_CFLAGS}" + fi + + # now check for the header. + if test "${AST_BEANSTALK_FOUND}" = "yes"; then + BEANSTALK_LIB="${pbxlibdir} -lbeanstalk " + # if --with-BEANSTALK=DIR has been specified, use it. + if test "x${BEANSTALK_DIR}" != "x"; then + BEANSTALK_INCLUDE="-I${BEANSTALK_DIR}/include" + fi + BEANSTALK_INCLUDE="${BEANSTALK_INCLUDE} " + if test "xbeanstalk.h" = "x" ; then # no header, assume found + BEANSTALK_HEADER_FOUND="1" + else # check for the header + ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" + CPPFLAGS="${CPPFLAGS} ${BEANSTALK_INCLUDE}" + ac_fn_c_check_header_mongrel "$LINENO" "beanstalk.h" "ac_cv_header_beanstalk_h" "$ac_includes_default" +if test "x$ac_cv_header_beanstalk_h" = xyes; then : + BEANSTALK_HEADER_FOUND=1 +else + BEANSTALK_HEADER_FOUND=0 +fi + + + CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}" + fi + if test "x${BEANSTALK_HEADER_FOUND}" = "x0" ; then + BEANSTALK_LIB="" + BEANSTALK_INCLUDE="" + else + if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library + BEANSTALK_LIB="" + fi + PBX_BEANSTALK=1 + cat >>confdefs.h <<_ACEOF +#define HAVE_BEANSTALK 1 +_ACEOF + + fi + fi +fi + + + # possible places for oss definitions if test "x${PBX_OSS}" != "x1" -a "${USE_OSS}" != "no"; then diff --git a/configure.ac b/configure.ac index 23ff5265a..47c6b26d4 100644 --- a/configure.ac +++ b/configure.ac @@ -507,6 +507,7 @@ AST_EXT_LIB_SETUP([OPUSFILE], [Opusfile], [opusfile]) AST_EXT_LIB_SETUP([OSPTK], [OSP Toolkit], [osptk]) AST_EXT_LIB_SETUP([OSS], [Open Sound System], [oss]) AST_EXT_LIB_SETUP([PGSQL], [PostgreSQL], [postgres]) +AST_EXT_LIB_SETUP([BEANSTALK], [Beanstalk Job Queue], [beanstalk]) if test "x${PBX_PJPROJECT}" != "x1" ; then AST_EXT_LIB_SETUP([PJPROJECT], [PJPROJECT], [pjproject]) @@ -1149,7 +1150,14 @@ AST_GCC_ATTRIBUTE(sentinel) AST_GCC_ATTRIBUTE(warn_unused_result) AST_GCC_ATTRIBUTE(may_alias) AST_GCC_ATTRIBUTE(constructor) +if test "$ax_cv_have_func_attribute_constructor" != "1"; then + AC_MSG_ERROR([*** Function constructor attribute is not supported by your compiler.]) +fi + AST_GCC_ATTRIBUTE(destructor) +if test "$ax_cv_have_func_attribute_destructor" != "1"; then + AC_MSG_ERROR([*** Function destructor attribute is not supported by your compiler.]) +fi AST_GCC_ATTRIBUTE(noreturn,noreturn) AC_MSG_CHECKING(for -fsanitize=address support) @@ -2188,6 +2196,8 @@ AST_EXT_LIB_CHECK([BKTR], [c], [backtrace], [execinfo.h]) AST_EXT_LIB_CHECK([BLUETOOTH], [bluetooth], [ba2str], [bluetooth/bluetooth.h]) +AST_EXT_LIB_CHECK([BEANSTALK], [beanstalk], [bs_version], [beanstalk.h]) + # possible places for oss definitions AST_EXT_LIB_CHECK([OSS], [ossaudio], [], [linux/soundcard.h]) AST_EXT_LIB_CHECK([OSS], [ossaudio], [], [sys/soundcard.h]) diff --git a/contrib/scripts/ast_coredumper b/contrib/scripts/ast_coredumper index 9b01a4239..884ede71f 100755 --- a/contrib/scripts/ast_coredumper +++ b/contrib/scripts/ast_coredumper @@ -15,7 +15,7 @@ SYNOPSIS $prog [ --help ] [ --running | --RUNNING ] [ --latest ] [ --tarball-coredumps ] [ --delete-coredumps-after ] [ --tarball-results ] [ --delete-results-after ] - [ --tarball-uniqueid="<uniqueid>" ] + [ --tarball-config ] [ --tarball-uniqueid="<uniqueid>" ] [ --no-default-search ] [ --append-coredumps ] [ --asterisk-bin="path" ] [ <coredump> | <pattern> ... ] @@ -51,7 +51,8 @@ DESCRIPTION Create a coredump from the running asterisk instance and process it along with any other coredumps found (if any). WARNING: This WILL interrupt call processing. You will be - asked to confirm. + asked to confirm. The coredump will be written to /tmp if + $OUTPUTDIR is not defined. --RUNNING Same as --running but without the confirmation prompt. @@ -69,10 +70,11 @@ DESCRIPTION /usr/sbin/asterisk, /usr/lib(64)/libasterisk* and /usr/lib(64)/asterisk as those files are needed to properly examine the coredump. The file will be named - /tmp/asterisk.<timestamp>.coredumps.tar.gz or - /tmp/asterisk-<uniqueid>.coredumps.tar.gz if + $OUTPUTDIR/asterisk.<timestamp>.coredumps.tar.gz or + $OUTPUTDIR/asterisk-<uniqueid>.coredumps.tar.gz if --tarball-uniqueid was specified. WARNING: This file could 1gb in size! + Mutually exclusive with --tartball-results --delete-coredumps-after Deletes all processed coredumps regardless of whether @@ -81,7 +83,8 @@ DESCRIPTION --tarball-results Creates a gzipped tarball of all result files produced. The tarball name will be: - /tmp/asterisk.<timestamp>.results.tar.gz + $OUTPUTDIR/asterisk.<timestamp>.results.tar.gz + Mutually exclusive with --tartball-coredumps --delete-results-after Deletes all processed results regardless of whether @@ -89,6 +92,10 @@ DESCRIPTION to use this option unless you have also specified --tarball-results. + --tarball-config + Adds the contents of /etc/asterisk to the tarball created + with --tarball-coredumps or --tarball-results. + --tarball-uniqueid="<uniqueid>" Normally DATEFORMAT is used to make the tarballs unique but you can use your own unique id in the tarball names @@ -130,6 +137,10 @@ DESCRIPTION NOTES You must be root to use $prog. + $OUTPUTDIR can be read from the current environment or from the + ast_debug_tools.conf file described below. If not specified, + work products are placed in the same directory as the core file. + The script relies on not only bash, but also recent GNU date and gdb with python support. *BSD operating systems may require installation of the 'coreutils' and 'devel/gdb' packagess and minor @@ -166,6 +177,12 @@ FILES # anyway. COREDUMPS=(/tmp/core[-._]asterisk!(*.txt) /tmp/core[-._]\$(hostname)!(*.txt)) + # The directory to contain output files and work directories. + # For output from existing core files, the default is the + # directory that the core file is found in. For core files + # produced from a running process, the default is /tmp. + OUTPUTDIR=/some/directory + # Date command for the "running" coredump and tarballs. # DATEFORMAT will be executed to get the timestamp. # Don't put quotes around the format string or they'll be @@ -227,6 +244,13 @@ if [ -z "$GDB" ] ; then exit 1 fi +if [ -n "$OUTPUTDIR" ] ; then + if [ ! -d "$OUTPUTDIR" ] ; then + echo "OUTPUTDIR $OUTPUTDIR doesn't exists or is not a directory" + exit 1 + fi +fi + if [ ${#COREDUMPS[@]} -eq 0 ] ; then COREDUMPS+=(/tmp/core[-._]asterisk!(*.txt) /tmp/core[-._]$(hostname)!(*.txt)) fi @@ -326,6 +350,10 @@ fi # Timestamp to use for output files df=${tarball_uniqueid:-$(${DATEFORMAT})} +if [ -z "$asterisk_bin" ]; then + asterisk_bin=$(which asterisk) +fi + if $running || $RUNNING ; then # We need to go through some gyrations to find the pid of the running # MAIN asterisk process and not someone or something running asterisk -r. @@ -351,9 +379,9 @@ if $running || $RUNNING ; then read -p "WARNING: Taking a core dump of the running asterisk instance will suspend call processing while the dump is saved. Do you wish to continue? (y/N) " answer fi if [[ "$answer" =~ ^[Yy] ]] ; then - cf="/tmp/core-asterisk-running-$df" + cf="${OUTPUTDIR:-/tmp}/core-asterisk-running-$df" echo "Dumping running asterisk process to $cf" - ${GDB} -p $pid -q --batch --ex "gcore $cf" >/dev/null 2>&1 + ${GDB} ${asterisk_bin} -p $pid -q --batch --ex "gcore $cf" >/dev/null 2>&1 COREDUMPS+=("$cf") else echo "Skipping dump of running process" @@ -370,20 +398,22 @@ fi # and save them to /tmp/.gdbinit ss=`egrep -n "^#@@@SCRIPTSTART@@@" $0 |cut -f1 -d:` -tail -n +${ss} $0 >/tmp/.ast_coredumper.gdbinit +tail -n +${ss} $0 >${OUTPUTDIR:-/tmp}/.ast_coredumper.gdbinit # Now iterate over the coredumps and dump the debugging info for i in ${!COREDUMPS[@]} ; do cf=${COREDUMPS[$i]} echo "Processing $cf" - if [ -z "$asterisk_bin" ]; then - asterisk_bin=$(which asterisk) - fi - ${GDB} -n --batch -q --ex "source /tmp/.ast_coredumper.gdbinit" "$asterisk_bin" "$cf" 2>/dev/null | ( + + cfdir=`dirname ${cf}` + cfname=`basename ${cf}` + outputdir=${OUTPUTDIR:-${cfdir}} + + ${GDB} -n --batch -q --ex "source ${OUTPUTDIR:-/tmp}/.ast_coredumper.gdbinit" "$asterisk_bin" "$cf" 2>/dev/null | ( of=/dev/null while IFS= read line ; do if [[ "$line" =~ !@!@!@!\ ([^\ ]+)\ !@!@!@! ]] ; then - of=${cf}-${BASH_REMATCH[1]} + of=${outputdir}/${cfname}-${BASH_REMATCH[1]} of=${of//:/-} rm -f "$of" echo "Creating $of" @@ -391,50 +421,58 @@ for i in ${!COREDUMPS[@]} ; do echo -e $"$line" >> "$of" done ) -done -if $tarball_coredumps ; then - tf=/tmp/asterisk-$df.coredumps.tar.gz - echo "Creating $tf" - dest=/tmp/asterisk-$df - rm -rf $dest 2>/dev/null || : - libdir=usr/lib - [ -d /usr/lib64 ] && libdir+=64 - mkdir -p $dest/tmp $dest/$libdir/asterisk $dest/etc $dest/usr/sbin - for i in ${!COREDUMPS[@]} ; do - ln -s "${COREDUMPS[@]}" $dest/"${COREDUMPS[@]}" - cp "${COREDUMPS[@]}"*.txt $dest/tmp/ - done - cp /etc/os-release $dest/etc/ - cp -a /$libdir/libasterisk* $dest/$libdir/ - cp -a /$libdir/asterisk/* $dest/$libdir/asterisk/ - cp -a /usr/sbin/asterisk $dest/usr/sbin - rm -rf $tf - tar -chzf $tf --transform="s/^[.]/$df/" -C $dest . - rm -rf $dest - echo "Created $tf" -fi + if $tarball_coredumps ; then + cfname=${cfname//:/-} + tf=${outputdir}/${cfname}.tar.gz + echo "Creating ${tf}" -if $delete_coredumps_after ; then - for i in ${!COREDUMPS[@]} ; do - rm -rf "${COREDUMPS[$i]}" - done -fi + dest=${outputdir}/${cfname}.output + rm -rf ${dest} 2>/dev/null || : -if $tarball_results ; then - tf=/tmp/asterisk-$df-results.tar - echo "Creating $tf.gz" - for i in ${!COREDUMPS[@]} ; do - tar -uvf $tf "${COREDUMPS[$i]//:/-}"-{brief,full,thread1,locks}.txt 2>/dev/null - done - gzip $tf -fi + libdir=usr/lib + [ -d /usr/lib64 ] && libdir+=64 + mkdir -p ${dest}/tmp ${dest}/${libdir}/asterisk ${dest}/etc ${dest}/usr/sbin -if $delete_results_after ; then - for i in ${!COREDUMPS[@]} ; do - rm -rf "${COREDUMPS[$i]//:/-}"-{brief,full,thread1,locks}.txt - done -fi + ln -s ${cf} ${dest}/tmp/${cfname} + cp ${outputdir}/${cfname}*.txt ${dest}/tmp/ + cp /etc/os-release ${dest}/etc/ + if $tarball_config ; then + cp -a /etc/asterisk ${dest}/etc/ + fi + cp -a /${libdir}/libasterisk* ${dest}/${libdir}/ + cp -a /${libdir}/asterisk/* ${dest}/${libdir}/asterisk/ + cp -a /usr/sbin/asterisk ${dest}/usr/sbin + rm -rf ${tf} + tar -chzf ${tf} --transform="s/^[.]/${cfname}/" -C ${dest} . + rm -rf ${dest} + echo "Created $tf" + elif $tarball_results ; then + cfname=${cfname//:/-} + tf=${outputdir}/${cfname}.tar.gz + echo "Creating ${tf}" + + dest=${outputdir}/${cfname}.output + rm -rf ${dest} 2>/dev/null || : + mkdir -p ${dest} + cp ${outputdir}/${cfname}*.txt ${dest}/ + if $tarball_config ; then + mkdir -p ${dest}/etc + cp -a /etc/asterisk ${dest}/etc/ + fi + tar -chzf ${tf} --transform="s/^[.]/${cfname}/" -C ${dest} . + rm -rf ${dest} + echo "Created $tf" + fi + +if $delete_coredumps_after ; then + rm -rf "${cf}" + fi + + if $delete_results_after ; then + rm -rf "${cf//:/-}"-{brief,full,thread1,locks}.txt + fi +done exit @@ -463,6 +501,9 @@ class DumpAsteriskCommand(gdb.Command): try: gdb.execute("p $_siginfo", from_tty) gdb.execute("info signal $_siginfo.si_signo") + except: + pass + try: gdb.execute("thread apply 1 bt full", from_tty) except: pass @@ -470,6 +511,9 @@ class DumpAsteriskCommand(gdb.Command): try: gdb.execute("p $_siginfo", from_tty) gdb.execute("info signal $_siginfo.si_signo") + except: + pass + try: gdb.execute("thread apply all bt", from_tty) except: pass @@ -477,6 +521,9 @@ class DumpAsteriskCommand(gdb.Command): try: gdb.execute("p $_siginfo", from_tty) gdb.execute("info signal $_siginfo.si_signo") + except: + pass + try: gdb.execute("thread apply all bt full", from_tty) except: pass @@ -484,6 +531,9 @@ class DumpAsteriskCommand(gdb.Command): try: gdb.execute("p $_siginfo", from_tty) gdb.execute("info signal $_siginfo.si_signo") + except: + pass + try: gdb.execute("show_locks", from_tty) except: pass diff --git a/include/asterisk/alertpipe.h b/include/asterisk/alertpipe.h index 5ff854ce8..09c335829 100644 --- a/include/asterisk/alertpipe.h +++ b/include/asterisk/alertpipe.h @@ -65,7 +65,8 @@ ast_alert_status_t ast_alertpipe_read(int alert_pipe[2]); * * \param p a two-element array containing the alert pipe's file descriptors * - * \return see write(2) + * \retval 0 Success + * \retval 1 Failure */ ssize_t ast_alertpipe_write(int alert_pipe[2]); diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in index cfa260b21..4700d4092 100644 --- a/include/asterisk/autoconfig.h.in +++ b/include/asterisk/autoconfig.h.in @@ -121,6 +121,9 @@ attribute. */ #undef HAVE_ATTRIBUTE_warn_unused_result +/* Define to 1 if you have the Beanstalk Job Queue library. */ +#undef HAVE_BEANSTALK + /* Define to 1 if you have the Debug symbol decoding library. */ #undef HAVE_BFD diff --git a/include/asterisk/http_websocket.h b/include/asterisk/http_websocket.h index cd49dbe48..2180ef46b 100644 --- a/include/asterisk/http_websocket.h +++ b/include/asterisk/http_websocket.h @@ -345,6 +345,15 @@ AST_OPTIONAL_API(int, ast_websocket_fd, (struct ast_websocket *session), { errno AST_OPTIONAL_API(struct ast_sockaddr *, ast_websocket_remote_address, (struct ast_websocket *session), {return NULL;}); /*! + * \brief Get the local address for a WebSocket connection session. + * + * \retval ast_sockaddr Local address + * + * \since 13.19.0 + */ +AST_OPTIONAL_API(struct ast_sockaddr *, ast_websocket_local_address, (struct ast_websocket *session), {return NULL;}); + +/*! * \brief Get whether the WebSocket session is using a secure transport or not. * * \retval 0 if unsecure diff --git a/include/asterisk/optional_api.h b/include/asterisk/optional_api.h index 1118dc7cd..1ac765162 100644 --- a/include/asterisk/optional_api.h +++ b/include/asterisk/optional_api.h @@ -107,10 +107,6 @@ #if defined(OPTIONAL_API) -#if !defined(HAVE_ATTRIBUTE_constructor) || !defined(HAVE_ATTRIBUTE_constructor) -#error OPTIONAL_API requires compiler constructor/destructor support -#endif - /*! * \internal * \brief Function pointer to an optional API function. diff --git a/include/asterisk/options.h b/include/asterisk/options.h index 0a20f10a8..878748d16 100644 --- a/include/asterisk/options.h +++ b/include/asterisk/options.h @@ -66,6 +66,8 @@ enum ast_option_flags { AST_OPT_FLAG_CACHE_RECORD_FILES = (1 << 13), /*! Display timestamp in CLI verbose output */ AST_OPT_FLAG_TIMESTAMP = (1 << 14), + /*! Cache media frames for performance */ + AST_OPT_FLAG_CACHE_MEDIA_FRAMES = (1 << 15), /*! Reconnect */ AST_OPT_FLAG_RECONNECT = (1 << 16), /*! Transmit Silence during Record() and DTMF Generation */ @@ -99,7 +101,7 @@ enum ast_option_flags { }; /*! These are the options that set by default when Asterisk starts */ -#define AST_DEFAULT_OPTIONS AST_OPT_FLAG_TRANSCODE_VIA_SLIN +#define AST_DEFAULT_OPTIONS (AST_OPT_FLAG_TRANSCODE_VIA_SLIN | AST_OPT_FLAG_CACHE_MEDIA_FRAMES) #define ast_opt_exec_includes ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES) #define ast_opt_no_fork ast_test_flag(&ast_options, AST_OPT_FLAG_NO_FORK) @@ -116,6 +118,7 @@ enum ast_option_flags { #define ast_opt_stdexten_macro ast_test_flag(&ast_options, AST_OPT_FLAG_STDEXTEN_MACRO) #define ast_opt_dump_core ast_test_flag(&ast_options, AST_OPT_FLAG_DUMP_CORE) #define ast_opt_cache_record_files ast_test_flag(&ast_options, AST_OPT_FLAG_CACHE_RECORD_FILES) +#define ast_opt_cache_media_frames ast_test_flag(&ast_options, AST_OPT_FLAG_CACHE_MEDIA_FRAMES) #define ast_opt_timestamp ast_test_flag(&ast_options, AST_OPT_FLAG_TIMESTAMP) #define ast_opt_reconnect ast_test_flag(&ast_options, AST_OPT_FLAG_RECONNECT) #define ast_opt_transmit_silence ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSMIT_SILENCE) diff --git a/include/asterisk/sorcery.h b/include/asterisk/sorcery.h index bfb2c39ad..bafca5fb6 100644 --- a/include/asterisk/sorcery.h +++ b/include/asterisk/sorcery.h @@ -298,6 +298,14 @@ struct ast_sorcery_wizard { /*! \brief Callback for retrieving multiple objects using a regex on their id */ void (*retrieve_regex)(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex); + /*! \brief Optional callback for retrieving multiple objects by matching their id with a prefix */ + void (*retrieve_prefix)(const struct ast_sorcery *sorcery, + void *data, + const char *type, + struct ao2_container *objects, + const char *prefix, + const size_t prefix_len); + /*! \brief Optional callback for retrieving an object using fields */ void *(*retrieve_fields)(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields); @@ -1241,6 +1249,22 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *sorcery, const char *type, const char *regex); /*! + * \brief Retrieve multiple objects whose id begins with the specified prefix + * \since 13.19.0 + * + * \param sorcery Pointer to a sorcery structure + * \param type Type of object to retrieve + * \param prefix Object id prefix + * \param prefix_len The length of prefix in bytes + * + * \retval non-NULL if error occurs + * \retval NULL success + * + * \note The prefix is matched in a case sensitive manner. + */ +struct ao2_container *ast_sorcery_retrieve_by_prefix(const struct ast_sorcery *sorcery, const char *type, const char *prefix, const size_t prefix_len); + +/*! * \brief Update an object * * \param sorcery Pointer to a sorcery structure diff --git a/include/asterisk/vector.h b/include/asterisk/vector.h index 68ce13065..8bd1cefef 100644 --- a/include/asterisk/vector.h +++ b/include/asterisk/vector.h @@ -51,6 +51,9 @@ /*! \brief Integer vector definition */ AST_VECTOR(ast_vector_int, int); +/*! \brief String vector definition */ +AST_VECTOR(ast_vector_string, char *); + /*! * \brief Define a vector structure with a read/write lock * @@ -91,6 +94,26 @@ AST_VECTOR(ast_vector_int, int); }) /*! + * \brief Steal the elements from a vector and reinitialize. + * + * \param vec Vector to operate on. + * + * This allows you to use vector.h to construct a list and use the + * data as a bare array. + * + * \note The stolen array must eventually be released using ast_free. + * + * \warning AST_VECTOR_SIZE and AST_VECTOR_MAX_SIZE are both reset + * to 0. If either are needed they must be saved to a local + * variable before stealing the elements. + */ +#define AST_VECTOR_STEAL_ELEMENTS(vec) ({ \ + typeof((vec)->elems) __elems = (vec)->elems; \ + AST_VECTOR_INIT((vec), 0); \ + (__elems); \ +}) + +/*! * \brief Initialize a vector with a read/write lock * * If \a size is 0, then no space will be allocated until the vector is diff --git a/main/aoc.c b/main/aoc.c index 451b21973..3487948df 100644 --- a/main/aoc.c +++ b/main/aoc.c @@ -1860,7 +1860,10 @@ static void aoc_publish_blob(struct ast_channel *chan, struct stasis_message_typ msg = stasis_message_create(msg_type, aoc_event); ao2_ref(aoc_event, -1); - stasis_publish(ast_manager_get_topic(), msg); + if (msg) { + stasis_publish(ast_manager_get_topic(), msg); + ao2_ref(msg, -1); + } } static struct ast_manager_event_blob *aoc_to_ami(struct stasis_message *message, diff --git a/main/asterisk.c b/main/asterisk.c index d55594983..77046f272 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -607,6 +607,9 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c ast_cli(a->fd, " Transmit silence during rec: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSMIT_SILENCE) ? "Enabled" : "Disabled"); ast_cli(a->fd, " Generic PLC: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_GENERIC_PLC) ? "Enabled" : "Disabled"); ast_cli(a->fd, " Min DTMF duration:: %u\n", option_dtmfminduration); +#if !defined(LOW_MEMORY) + ast_cli(a->fd, " Cache media frames: %s\n", ast_opt_cache_media_frames ? "Enabled" : "Disabled"); +#endif ast_cli(a->fd, " RTP use dynamic payloads: %u\n", ast_option_rtpusedynamic); if (ast_option_rtpptdynamic == AST_RTP_PT_LAST_REASSIGN) { @@ -1761,15 +1764,13 @@ static struct sigaction urg_handler = { static void _hup_handler(int num) { - int a = 0, save_errno = errno; + int save_errno = errno; printf("Received HUP signal -- Reloading configs\n"); if (restartnow) execvp(_argv[0], _argv); sig_flags.need_reload = 1; - if (sig_alert_pipe[1] != -1) { - if (write(sig_alert_pipe[1], &a, sizeof(a)) < 0) { - fprintf(stderr, "hup_handler: write() failed: %s\n", strerror(errno)); - } + if (ast_alertpipe_write(sig_alert_pipe)) { + fprintf(stderr, "hup_handler: write() failed: %s\n", strerror(errno)); } errno = save_errno; } @@ -2169,10 +2170,7 @@ static void really_quit(int num, shutdown_nice_t niceness, int restart) close(ast_consock); if (!ast_opt_remote) unlink(ast_config_AST_PID); - if (sig_alert_pipe[0]) - close(sig_alert_pipe[0]); - if (sig_alert_pipe[1]) - close(sig_alert_pipe[1]); + ast_alertpipe_close(sig_alert_pipe); printf("%s", term_quit()); if (restart) { int i; @@ -2208,12 +2206,9 @@ static void really_quit(int num, shutdown_nice_t niceness, int restart) static void __quit_handler(int num) { - int a = 0; sig_flags.need_quit = 1; - if (sig_alert_pipe[1] != -1) { - if (write(sig_alert_pipe[1], &a, sizeof(a)) < 0) { - fprintf(stderr, "quit_handler: write() failed: %s\n", strerror(errno)); - } + if (ast_alertpipe_write(sig_alert_pipe)) { + fprintf(stderr, "quit_handler: write() failed: %s\n", strerror(errno)); } /* There is no need to restore the signal handler here, since the app * is going to exit */ @@ -2466,16 +2461,6 @@ static char *handle_version(struct ast_cli_entry *e, int cmd, struct ast_cli_arg return CLI_SUCCESS; } -#if 0 -static int handle_quit(int fd, int argc, char *argv[]) -{ - if (argc != 1) - return RESULT_SHOWUSAGE; - quit_handler(0, SHUTDOWN_NORMAL, 0); - return RESULT_SUCCESS; -} -#endif - static char *handle_stop_now(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { switch (cmd) { @@ -3665,7 +3650,6 @@ static void ast_readconfig(void) ast_copy_string(cfg_paths.agi_dir, v->value, sizeof(cfg_paths.agi_dir)); } else if (!strcasecmp(v->name, "astrundir")) { snprintf(cfg_paths.pid_path, sizeof(cfg_paths.pid_path), "%s/%s", v->value, "asterisk.pid"); - snprintf(cfg_paths.socket_path, sizeof(cfg_paths.socket_path), "%s/%s", v->value, ast_config_AST_CTL); ast_copy_string(cfg_paths.run_dir, v->value, sizeof(cfg_paths.run_dir)); } else if (!strcasecmp(v->name, "astmoddir")) { ast_copy_string(cfg_paths.module_dir, v->value, sizeof(cfg_paths.module_dir)); @@ -3674,6 +3658,10 @@ static void ast_readconfig(void) } } + /* Combine astrundir and astctl settings. */ + snprintf(cfg_paths.socket_path, sizeof(cfg_paths.socket_path), "%s/%s", + ast_config_AST_RUN_DIR, ast_config_AST_CTL); + for (v = ast_variable_browse(cfg, "options"); v; v = v->next) { /* verbose level (-v at startup) */ if (!strcasecmp(v->name, "verbose")) { @@ -3724,6 +3712,11 @@ static void ast_readconfig(void) /* Cache recorded sound files to another directory during recording */ } else if (!strcasecmp(v->name, "cache_record_files")) { ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_CACHE_RECORD_FILES); +#if !defined(LOW_MEMORY) + /* Cache media frames for performance */ + } else if (!strcasecmp(v->name, "cache_media_frames")) { + ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_CACHE_MEDIA_FRAMES); +#endif /* Specify cache directory */ } else if (!strcasecmp(v->name, "record_cache_dir")) { ast_copy_string(record_cache_dir, v->value, AST_CACHE_DIR_LEN); @@ -3887,7 +3880,7 @@ static void *monitor_sig_flags(void *unused) { for (;;) { struct pollfd p = { sig_alert_pipe[0], POLLIN, 0 }; - int a; + ast_poll(&p, 1, -1); if (sig_flags.need_reload) { sig_flags.need_reload = 0; @@ -3902,8 +3895,7 @@ static void *monitor_sig_flags(void *unused) quit_handler(0, SHUTDOWN_NORMAL, 0); } } - if (read(sig_alert_pipe[0], &a, sizeof(a)) != sizeof(a)) { - } + ast_alertpipe_read(sig_alert_pipe); } return NULL; @@ -4710,9 +4702,7 @@ static void asterisk_daemon(int isroot, const char *runuser, const char *rungrou consolethread = pthread_self(); } - if (pipe(sig_alert_pipe)) { - sig_alert_pipe[0] = sig_alert_pipe[1] = -1; - } + ast_alertpipe_init(sig_alert_pipe); ast_process_pending_reloads(); diff --git a/main/audiohook.c b/main/audiohook.c index 2cba2de6e..04a379fef 100644 --- a/main/audiohook.c +++ b/main/audiohook.c @@ -950,7 +950,9 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st * rely on actual media being present to do things. */ if (!middle_frame->data.ptr) { - ast_frfree(middle_frame); + if (middle_frame != start_frame) { + ast_frfree(middle_frame); + } return start_frame; } diff --git a/main/frame.c b/main/frame.c index c24cc8f78..dd47f42d0 100644 --- a/main/frame.c +++ b/main/frame.c @@ -120,14 +120,18 @@ static void __frame_free(struct ast_frame *fr, int cache) return; #if !defined(LOW_MEMORY) - if (cache && fr->mallocd == AST_MALLOCD_HDR) { + if (fr->mallocd == AST_MALLOCD_HDR + && cache + && ast_opt_cache_media_frames) { /* Cool, only the header is malloc'd, let's just cache those for now * to keep things simple... */ struct ast_frame_cache *frames; - if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames))) && - (frames->size < FRAME_CACHE_MAX_SIZE)) { - if ((fr->frametype == AST_FRAME_VOICE) || (fr->frametype == AST_FRAME_VIDEO) || - (fr->frametype == AST_FRAME_IMAGE)) { + + frames = ast_threadstorage_get(&frame_cache, sizeof(*frames)); + if (frames && frames->size < FRAME_CACHE_MAX_SIZE) { + if (fr->frametype == AST_FRAME_VOICE + || fr->frametype == AST_FRAME_VIDEO + || fr->frametype == AST_FRAME_IMAGE) { ao2_cleanup(fr->subclass.format); } @@ -147,8 +151,9 @@ static void __frame_free(struct ast_frame *fr, int cache) ast_free((void *) fr->src); } if (fr->mallocd & AST_MALLOCD_HDR) { - if ((fr->frametype == AST_FRAME_VOICE) || (fr->frametype == AST_FRAME_VIDEO) || - (fr->frametype == AST_FRAME_IMAGE)) { + if (fr->frametype == AST_FRAME_VOICE + || fr->frametype == AST_FRAME_VIDEO + || fr->frametype == AST_FRAME_IMAGE) { ao2_cleanup(fr->subclass.format); } @@ -163,18 +168,16 @@ void ast_frame_free(struct ast_frame *frame, int cache) { struct ast_frame *next; - for (next = AST_LIST_NEXT(frame, frame_list); - frame; - frame = next, next = frame ? AST_LIST_NEXT(frame, frame_list) : NULL) { + while (frame) { + next = AST_LIST_NEXT(frame, frame_list); __frame_free(frame, cache); + frame = next; } } void ast_frame_dtor(struct ast_frame *f) { - if (f) { - ast_frfree(f); - } + ast_frfree(f); } /*! diff --git a/main/plc.c b/main/plc.c index b649357dc..739f7276d 100644 --- a/main/plc.c +++ b/main/plc.c @@ -96,7 +96,7 @@ static void normalise_history(plc_state_t *s) if (s->buf_ptr == 0) return; memcpy(tmp, s->history, sizeof(int16_t)*s->buf_ptr); - memcpy(s->history, s->history + s->buf_ptr, sizeof(int16_t) * (PLC_HISTORY_LEN - s->buf_ptr)); + memmove(s->history, s->history + s->buf_ptr, sizeof(int16_t) * (PLC_HISTORY_LEN - s->buf_ptr)); memcpy(s->history + PLC_HISTORY_LEN - s->buf_ptr, tmp, sizeof(int16_t) * s->buf_ptr); s->buf_ptr = 0; } diff --git a/main/sdp_state.c b/main/sdp_state.c index a77d96da5..2b75cc2a1 100644 --- a/main/sdp_state.c +++ b/main/sdp_state.c @@ -1255,7 +1255,10 @@ static int sdp_merge_streams_match( return -1; } idx = AST_VECTOR_GET(current_vect, current_idx); - ast_stream_topology_set_stream(merged_topology, idx, merged_stream); + if (ast_stream_topology_set_stream(merged_topology, idx, merged_stream)) { + ast_stream_free(merged_stream); + return -1; + } /* * The current_stream cannot be considered a backfill_candidate @@ -1400,7 +1403,10 @@ static struct ast_stream_topology *merge_local_topologies( if (!merged_stream) { goto fail; } - ast_stream_topology_set_stream(merged_topology, idx, merged_stream); + if (ast_stream_topology_set_stream(merged_topology, idx, merged_stream)) { + ast_stream_free(merged_stream); + goto fail; + } } /* Backfill new update stream slots into pre-existing declined current stream slots */ @@ -1438,7 +1444,10 @@ static struct ast_stream_topology *merge_local_topologies( } /* Add the new stream into the backfill stream slot. */ - ast_stream_topology_set_stream(merged_topology, current_idx, merged_stream); + if (ast_stream_topology_set_stream(merged_topology, current_idx, merged_stream)) { + ast_stream_free(merged_stream); + goto fail; + } backfill_candidate[current_idx] = 0; } diff --git a/main/sorcery.c b/main/sorcery.c index 01b77918d..51b55c5a2 100644 --- a/main/sorcery.c +++ b/main/sorcery.c @@ -2036,6 +2036,36 @@ struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *so return objects; } +struct ao2_container *ast_sorcery_retrieve_by_prefix(const struct ast_sorcery *sorcery, const char *type, const char *prefix, const size_t prefix_len) +{ + RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup); + struct ao2_container *objects; + int i; + + if (!object_type || !(objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) { + return NULL; + } + + AST_VECTOR_RW_RDLOCK(&object_type->wizards); + for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) { + struct ast_sorcery_object_wizard *wizard = + AST_VECTOR_GET(&object_type->wizards, i); + + if (!wizard->wizard->callbacks.retrieve_prefix) { + continue; + } + + wizard->wizard->callbacks.retrieve_prefix(sorcery, wizard->data, object_type->name, objects, prefix, prefix_len); + + if (wizard->caching && ao2_container_count(objects)) { + break; + } + } + AST_VECTOR_RW_UNLOCK(&object_type->wizards); + + return objects; +} + /*! \brief Internal function which returns if the wizard has created the object */ static int sorcery_wizard_create(const struct ast_sorcery_object_wizard *object_wizard, const struct sorcery_details *details) { diff --git a/main/stasis_bridges.c b/main/stasis_bridges.c index 7f53bfe2d..d197e4d2d 100644 --- a/main/stasis_bridges.c +++ b/main/stasis_bridges.c @@ -868,6 +868,8 @@ static struct ast_json *attended_transfer_to_json(struct stasis_message *msg, if (transfer_msg->transferee) { json_transferee = ast_channel_snapshot_to_json(transfer_msg->transferee, sanitize); if (!json_transferee) { + ast_json_unref(json_transferer2); + ast_json_unref(json_transferer1); return NULL; } } @@ -875,6 +877,9 @@ static struct ast_json *attended_transfer_to_json(struct stasis_message *msg, if (transfer_msg->target) { json_target = ast_channel_snapshot_to_json(transfer_msg->target, sanitize); if (!json_target) { + ast_json_unref(json_transferee); + ast_json_unref(json_transferer2); + ast_json_unref(json_transferer1); return NULL; } } @@ -887,9 +892,12 @@ static struct ast_json *attended_transfer_to_json(struct stasis_message *msg, "result", result_strs[transfer_msg->result], "is_external", ast_json_boolean(transfer_msg->is_external)); if (!out) { + ast_json_unref(json_target); + ast_json_unref(json_transferee); return NULL; } if (json_transferee && ast_json_object_set(out, "transferee", json_transferee)) { + ast_json_unref(json_target); return NULL; } if (json_target && ast_json_object_set(out, "transfer_target", json_target)) { diff --git a/makeopts.in b/makeopts.in index e25b614e0..1d31c29f9 100644 --- a/makeopts.in +++ b/makeopts.in @@ -388,3 +388,6 @@ TIMERFD_INCLUDE=@TIMERFD_INCLUDE@ SNDFILE_INCLUDE=@SNDFILE_INCLUDE@ SNDFILE_LIB=@SNDFILE_LIB@ + +BEANSTALK_INCLUDE=@BEANSTALK_INCLUDE@ +BEANSTALK_LIB=@BEANSTALK_LIB@ diff --git a/menuselect/.gitignore b/menuselect/.gitignore index ded8d2d6e..05f4778ff 100644 --- a/menuselect/.gitignore +++ b/menuselect/.gitignore @@ -1,3 +1,4 @@ +aclocal.m4 autoconfig.h cmenuselect config.log diff --git a/menuselect/aclocal.m4 b/menuselect/aclocal.m4 deleted file mode 100644 index e67774c06..000000000 --- a/menuselect/aclocal.m4 +++ /dev/null @@ -1,296 +0,0 @@ -# generated automatically by aclocal 1.15 -*- Autoconf -*- - -# Copyright (C) 1996-2014 Free Software Foundation, Inc. - -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) -dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- -dnl serial 11 (pkg-config-0.29) -dnl -dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>. -dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com> -dnl -dnl This program is free software; you can redistribute it and/or modify -dnl it under the terms of the GNU General Public License as published by -dnl the Free Software Foundation; either version 2 of the License, or -dnl (at your option) any later version. -dnl -dnl This program is distributed in the hope that it will be useful, but -dnl WITHOUT ANY WARRANTY; without even the implied warranty of -dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -dnl General Public License for more details. -dnl -dnl You should have received a copy of the GNU General Public License -dnl along with this program; if not, write to the Free Software -dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -dnl 02111-1307, USA. -dnl -dnl As a special exception to the GNU General Public License, if you -dnl distribute this file as part of a program that contains a -dnl configuration script generated by Autoconf, you may include it under -dnl the same distribution terms that you use for the rest of that -dnl program. - -dnl PKG_PREREQ(MIN-VERSION) -dnl ----------------------- -dnl Since: 0.29 -dnl -dnl Verify that the version of the pkg-config macros are at least -dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's -dnl installed version of pkg-config, this checks the developer's version -dnl of pkg.m4 when generating configure. -dnl -dnl To ensure that this macro is defined, also add: -dnl m4_ifndef([PKG_PREREQ], -dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) -dnl -dnl See the "Since" comment for each macro you use to see what version -dnl of the macros you require. -m4_defun([PKG_PREREQ], -[m4_define([PKG_MACROS_VERSION], [0.29]) -m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, - [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) -])dnl PKG_PREREQ - -dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) -dnl ---------------------------------- -dnl Since: 0.16 -dnl -dnl Search for the pkg-config tool and set the PKG_CONFIG variable to -dnl first found in the path. Checks that the version of pkg-config found -dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is -dnl used since that's the first version where most current features of -dnl pkg-config existed. -AC_DEFUN([PKG_PROG_PKG_CONFIG], -[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) -m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) -m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) -AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) -AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) -AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) - -if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then - AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) -fi -if test -n "$PKG_CONFIG"; then - _pkg_min_version=m4_default([$1], [0.9.0]) - AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) - if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - PKG_CONFIG="" - fi -fi[]dnl -])dnl PKG_PROG_PKG_CONFIG - -dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -dnl ------------------------------------------------------------------- -dnl Since: 0.18 -dnl -dnl Check to see whether a particular set of modules exists. Similar to -dnl PKG_CHECK_MODULES(), but does not set variables or print errors. -dnl -dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) -dnl only at the first occurence in configure.ac, so if the first place -dnl it's called might be skipped (such as if it is within an "if", you -dnl have to call PKG_CHECK_EXISTS manually -AC_DEFUN([PKG_CHECK_EXISTS], -[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl -if test -n "$PKG_CONFIG" && \ - AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then - m4_default([$2], [:]) -m4_ifvaln([$3], [else - $3])dnl -fi]) - -dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) -dnl --------------------------------------------- -dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting -dnl pkg_failed based on the result. -m4_define([_PKG_CONFIG], -[if test -n "$$1"; then - pkg_cv_[]$1="$$1" - elif test -n "$PKG_CONFIG"; then - PKG_CHECK_EXISTS([$3], - [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes ], - [pkg_failed=yes]) - else - pkg_failed=untried -fi[]dnl -])dnl _PKG_CONFIG - -dnl _PKG_SHORT_ERRORS_SUPPORTED -dnl --------------------------- -dnl Internal check to see if pkg-config supports short errors. -AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], -[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi[]dnl -])dnl _PKG_SHORT_ERRORS_SUPPORTED - - -dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], -dnl [ACTION-IF-NOT-FOUND]) -dnl -------------------------------------------------------------- -dnl Since: 0.4.0 -dnl -dnl Note that if there is a possibility the first call to -dnl PKG_CHECK_MODULES might not happen, you should be sure to include an -dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac -AC_DEFUN([PKG_CHECK_MODULES], -[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl -AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl -AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl - -pkg_failed=no -AC_MSG_CHECKING([for $1]) - -_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) -_PKG_CONFIG([$1][_LIBS], [libs], [$2]) - -m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS -and $1[]_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details.]) - -if test $pkg_failed = yes; then - AC_MSG_RESULT([no]) - _PKG_SHORT_ERRORS_SUPPORTED - if test $_pkg_short_errors_supported = yes; then - $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` - else - $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD - - m4_default([$4], [AC_MSG_ERROR( -[Package requirements ($2) were not met: - -$$1_PKG_ERRORS - -Consider adjusting the PKG_CONFIG_PATH environment variable if you -installed software in a non-standard prefix. - -_PKG_TEXT])[]dnl - ]) -elif test $pkg_failed = untried; then - AC_MSG_RESULT([no]) - m4_default([$4], [AC_MSG_FAILURE( -[The pkg-config script could not be found or is too old. Make sure it -is in your PATH or set the PKG_CONFIG environment variable to the full -path to pkg-config. - -_PKG_TEXT - -To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl - ]) -else - $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS - $1[]_LIBS=$pkg_cv_[]$1[]_LIBS - AC_MSG_RESULT([yes]) - $3 -fi[]dnl -])dnl PKG_CHECK_MODULES - - -dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], -dnl [ACTION-IF-NOT-FOUND]) -dnl --------------------------------------------------------------------- -dnl Since: 0.29 -dnl -dnl Checks for existence of MODULES and gathers its build flags with -dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags -dnl and VARIABLE-PREFIX_LIBS from --libs. -dnl -dnl Note that if there is a possibility the first call to -dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to -dnl include an explicit call to PKG_PROG_PKG_CONFIG in your -dnl configure.ac. -AC_DEFUN([PKG_CHECK_MODULES_STATIC], -[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl -_save_PKG_CONFIG=$PKG_CONFIG -PKG_CONFIG="$PKG_CONFIG --static" -PKG_CHECK_MODULES($@) -PKG_CONFIG=$_save_PKG_CONFIG[]dnl -])dnl PKG_CHECK_MODULES_STATIC - - -dnl PKG_INSTALLDIR([DIRECTORY]) -dnl ------------------------- -dnl Since: 0.27 -dnl -dnl Substitutes the variable pkgconfigdir as the location where a module -dnl should install pkg-config .pc files. By default the directory is -dnl $libdir/pkgconfig, but the default can be changed by passing -dnl DIRECTORY. The user can override through the --with-pkgconfigdir -dnl parameter. -AC_DEFUN([PKG_INSTALLDIR], -[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) -m4_pushdef([pkg_description], - [pkg-config installation directory @<:@]pkg_default[@:>@]) -AC_ARG_WITH([pkgconfigdir], - [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, - [with_pkgconfigdir=]pkg_default) -AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) -m4_popdef([pkg_default]) -m4_popdef([pkg_description]) -])dnl PKG_INSTALLDIR - - -dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) -dnl -------------------------------- -dnl Since: 0.27 -dnl -dnl Substitutes the variable noarch_pkgconfigdir as the location where a -dnl module should install arch-independent pkg-config .pc files. By -dnl default the directory is $datadir/pkgconfig, but the default can be -dnl changed by passing DIRECTORY. The user can override through the -dnl --with-noarch-pkgconfigdir parameter. -AC_DEFUN([PKG_NOARCH_INSTALLDIR], -[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) -m4_pushdef([pkg_description], - [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) -AC_ARG_WITH([noarch-pkgconfigdir], - [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, - [with_noarch_pkgconfigdir=]pkg_default) -AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) -m4_popdef([pkg_default]) -m4_popdef([pkg_description]) -])dnl PKG_NOARCH_INSTALLDIR - - -dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, -dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -dnl ------------------------------------------- -dnl Since: 0.28 -dnl -dnl Retrieves the value of the pkg-config variable for the given module. -AC_DEFUN([PKG_CHECK_VAR], -[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl -AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl - -_PKG_CONFIG([$1], [variable="][$3]["], [$2]) -AS_VAR_COPY([$1], [pkg_cv_][$1]) - -AS_VAR_IF([$1], [""], [$5], [$4])dnl -])dnl PKG_CHECK_VAR - -m4_include([../autoconf/ast_check_gnu_make.m4]) -m4_include([../autoconf/ast_ext_lib.m4]) -m4_include([../autoconf/ast_ext_tool_check.m4]) -m4_include([../autoconf/ast_gcc_attribute.m4]) -m4_include([../autoconf/ast_pkgconfig.m4]) -m4_include([../autoconf/ast_prog_sed.m4]) diff --git a/res/res_hep_pjsip.c b/res/res_hep_pjsip.c index 13efbfa6a..b5be4a102 100644 --- a/res/res_hep_pjsip.c +++ b/res/res_hep_pjsip.c @@ -89,35 +89,44 @@ static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) pjsip_cid_hdr *cid_hdr; pjsip_from_hdr *from_hdr; pjsip_to_hdr *to_hdr; - pjsip_tpmgr_fla2_param prm; capture_info = hepv3_create_capture_info(tdata->buf.start, (size_t)(tdata->buf.cur - tdata->buf.start)); if (!capture_info) { return PJ_SUCCESS; } - /* Attempt to determine what IP address will we send this packet out of */ - pjsip_tpmgr_fla2_param_default(&prm); - prm.tp_type = tdata->tp_info.transport->key.type; - pj_strset2(&prm.dst_host, tdata->tp_info.dst_name); - prm.local_if = PJ_TRUE; + if (!(tdata->tp_info.transport->flag & PJSIP_TRANSPORT_RELIABLE)) { + pjsip_tpmgr_fla2_param prm; - /* If we can't get the local address use what we have already */ - if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), tdata->pool, &prm) != PJ_SUCCESS) { - pj_sockaddr_print(&tdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3); - } else { - if (prm.tp_type & PJSIP_TRANSPORT_IPV6) { - snprintf(local_buf, sizeof(local_buf), "[%.*s]:%hu", - (int)pj_strlen(&prm.ret_addr), - pj_strbuf(&prm.ret_addr), - prm.ret_port); + /* Attempt to determine what IP address will we send this packet out of */ + pjsip_tpmgr_fla2_param_default(&prm); + prm.tp_type = tdata->tp_info.transport->key.type; + pj_strset2(&prm.dst_host, tdata->tp_info.dst_name); + prm.local_if = PJ_TRUE; + + /* If we can't get the local address use what we have already */ + if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), tdata->pool, &prm) != PJ_SUCCESS) { + pj_sockaddr_print(&tdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3); } else { - snprintf(local_buf, sizeof(local_buf), "%.*s:%hu", - (int)pj_strlen(&prm.ret_addr), - pj_strbuf(&prm.ret_addr), - prm.ret_port); + if (prm.tp_type & PJSIP_TRANSPORT_IPV6) { + snprintf(local_buf, sizeof(local_buf), "[%.*s]:%hu", + (int)pj_strlen(&prm.ret_addr), + pj_strbuf(&prm.ret_addr), + prm.ret_port); + } else { + snprintf(local_buf, sizeof(local_buf), "%.*s:%hu", + (int)pj_strlen(&prm.ret_addr), + pj_strbuf(&prm.ret_addr), + prm.ret_port); + } } + } else { + /* For reliable transports they can only ever come from the transport + * local address. + */ + pj_sockaddr_print(&tdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3); } + pj_sockaddr_print(&tdata->tp_info.dst_addr, remote_buf, sizeof(remote_buf), 3); cid_hdr = PJSIP_MSG_CID_HDR(tdata->msg); @@ -150,7 +159,6 @@ static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata) char remote_buf[256]; char *uuid; struct hepv3_capture_info *capture_info; - pjsip_tpmgr_fla2_param prm; capture_info = hepv3_create_capture_info(&rdata->pkt_info.packet, rdata->pkt_info.len); if (!capture_info) { @@ -162,27 +170,33 @@ static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata) } pj_sockaddr_print(&rdata->pkt_info.src_addr, remote_buf, sizeof(remote_buf), 3); - /* Attempt to determine what IP address we probably received this packet on */ - pjsip_tpmgr_fla2_param_default(&prm); - prm.tp_type = rdata->tp_info.transport->key.type; - pj_strset2(&prm.dst_host, rdata->pkt_info.src_name); - prm.local_if = PJ_TRUE; + if (!(rdata->tp_info.transport->flag & PJSIP_TRANSPORT_RELIABLE)) { + pjsip_tpmgr_fla2_param prm; - /* If we can't get the local address use what we have already */ - if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), rdata->tp_info.pool, &prm) != PJ_SUCCESS) { - pj_sockaddr_print(&rdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3); - } else { - if (prm.tp_type & PJSIP_TRANSPORT_IPV6) { - snprintf(local_buf, sizeof(local_buf), "[%.*s]:%hu", - (int)pj_strlen(&prm.ret_addr), - pj_strbuf(&prm.ret_addr), - prm.ret_port); + /* Attempt to determine what IP address we probably received this packet on */ + pjsip_tpmgr_fla2_param_default(&prm); + prm.tp_type = rdata->tp_info.transport->key.type; + pj_strset2(&prm.dst_host, rdata->pkt_info.src_name); + prm.local_if = PJ_TRUE; + + /* If we can't get the local address use what we have already */ + if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), rdata->tp_info.pool, &prm) != PJ_SUCCESS) { + pj_sockaddr_print(&rdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3); } else { - snprintf(local_buf, sizeof(local_buf), "%.*s:%hu", - (int)pj_strlen(&prm.ret_addr), - pj_strbuf(&prm.ret_addr), - prm.ret_port); + if (prm.tp_type & PJSIP_TRANSPORT_IPV6) { + snprintf(local_buf, sizeof(local_buf), "[%.*s]:%hu", + (int)pj_strlen(&prm.ret_addr), + pj_strbuf(&prm.ret_addr), + prm.ret_port); + } else { + snprintf(local_buf, sizeof(local_buf), "%.*s:%hu", + (int)pj_strlen(&prm.ret_addr), + pj_strbuf(&prm.ret_addr), + prm.ret_port); + } } + } else { + pj_sockaddr_print(&rdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3); } uuid = assign_uuid(&rdata->msg_info.cid->id, &rdata->msg_info.to->tag, &rdata->msg_info.from->tag); diff --git a/res/res_http_websocket.c b/res/res_http_websocket.c index 8e9aae9dd..c1f9a29d6 100644 --- a/res/res_http_websocket.c +++ b/res/res_http_websocket.c @@ -86,18 +86,19 @@ /*! \brief Structure definition for session */ struct ast_websocket { - struct ast_iostream *stream; /*!< iostream of the connection */ - struct ast_sockaddr address; /*!< Address of the remote client */ - enum ast_websocket_opcode opcode; /*!< Cached opcode for multi-frame messages */ - size_t payload_len; /*!< Length of the payload */ - char *payload; /*!< Pointer to the payload */ - size_t reconstruct; /*!< Number of bytes before a reconstructed payload will be returned and a new one started */ - int timeout; /*!< The timeout for operations on the socket */ - unsigned int secure:1; /*!< Bit to indicate that the transport is secure */ - unsigned int closing:1; /*!< Bit to indicate that the session is in the process of being closed */ - unsigned int close_sent:1; /*!< Bit to indicate that the session close opcode has been sent and no further data will be sent */ - struct websocket_client *client; /*!< Client object when connected as a client websocket */ - char session_id[AST_UUID_STR_LEN]; /*!< The identifier for the websocket session */ + struct ast_iostream *stream; /*!< iostream of the connection */ + struct ast_sockaddr remote_address; /*!< Address of the remote client */ + struct ast_sockaddr local_address; /*!< Our local address */ + enum ast_websocket_opcode opcode; /*!< Cached opcode for multi-frame messages */ + size_t payload_len; /*!< Length of the payload */ + char *payload; /*!< Pointer to the payload */ + size_t reconstruct; /*!< Number of bytes before a reconstructed payload will be returned and a new one started */ + int timeout; /*!< The timeout for operations on the socket */ + unsigned int secure:1; /*!< Bit to indicate that the transport is secure */ + unsigned int closing:1; /*!< Bit to indicate that the session is in the process of being closed */ + unsigned int close_sent:1; /*!< Bit to indicate that the session close opcode has been sent and no further data will be sent */ + struct websocket_client *client; /*!< Client object when connected as a client websocket */ + char session_id[AST_UUID_STR_LEN]; /*!< The identifier for the websocket session */ }; /*! \brief Hashing function for protocols */ @@ -183,7 +184,7 @@ static void session_destroy_fn(void *obj) ast_iostream_close(session->stream); session->stream = NULL; ast_verb(2, "WebSocket connection %s '%s' closed\n", session->client ? "to" : "from", - ast_sockaddr_stringify(&session->address)); + ast_sockaddr_stringify(&session->remote_address)); } } @@ -318,7 +319,7 @@ int AST_OPTIONAL_API_NAME(ast_websocket_close)(struct ast_websocket *session, ui ast_iostream_close(session->stream); session->stream = NULL; ast_verb(2, "WebSocket connection %s '%s' forcefully closed due to fatal write error\n", - session->client ? "to" : "from", ast_sockaddr_stringify(&session->address)); + session->client ? "to" : "from", ast_sockaddr_stringify(&session->remote_address)); } ao2_unlock(session); @@ -432,7 +433,12 @@ int AST_OPTIONAL_API_NAME(ast_websocket_fd)(struct ast_websocket *session) struct ast_sockaddr * AST_OPTIONAL_API_NAME(ast_websocket_remote_address)(struct ast_websocket *session) { - return &session->address; + return &session->remote_address; +} + +struct ast_sockaddr * AST_OPTIONAL_API_NAME(ast_websocket_local_address)(struct ast_websocket *session) +{ + return &session->local_address; } int AST_OPTIONAL_API_NAME(ast_websocket_is_secure)(struct ast_websocket *session) @@ -899,11 +905,21 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan return 0; } + /* Get our local address for the connected socket */ + if (ast_getsockname(ast_iostream_get_fd(ser->stream), &session->local_address)) { + ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - failed to get local address\n", + ast_sockaddr_stringify(&ser->remote_address)); + websocket_bad_request(ser); + ao2_ref(session, -1); + ao2_ref(protocol_handler, -1); + return 0; + } + ast_verb(2, "WebSocket connection from '%s' for protocol '%s' accepted using version '%d'\n", ast_sockaddr_stringify(&ser->remote_address), protocol ? : "", version); /* Populate the session with all the needed details */ session->stream = ser->stream; - ast_sockaddr_copy(&session->address, &ser->remote_address); + ast_sockaddr_copy(&session->remote_address, &ser->remote_address); session->opcode = -1; session->reconstruct = DEFAULT_RECONSTRUCTION_CEILING; session->secure = ast_iostream_get_ssl(ser->stream) ? 1 : 0; @@ -1357,7 +1373,7 @@ static enum ast_websocket_result websocket_client_connect(struct ast_websocket * ws->stream = ws->client->ser->stream; ws->secure = ast_iostream_get_ssl(ws->stream) ? 1 : 0; ws->client->ser->stream = NULL; - ast_sockaddr_copy(&ws->address, &ws->client->ser->remote_address); + ast_sockaddr_copy(&ws->remote_address, &ws->client->ser->remote_address); return WS_OK; } diff --git a/res/res_pjsip_history.c b/res/res_pjsip_history.c index 4e7dbd007..ed374d605 100644 --- a/res/res_pjsip_history.c +++ b/res/res_pjsip_history.c @@ -705,10 +705,13 @@ static pj_status_t history_on_tx_msg(pjsip_tx_data *tdata) pj_sockaddr_cp(&entry->dst, &tdata->tp_info.dst_addr); ast_mutex_lock(&history_lock); - AST_VECTOR_APPEND(&vector_history, entry); + if (AST_VECTOR_APPEND(&vector_history, entry)) { + ao2_ref(entry, -1); + entry = NULL; + } ast_mutex_unlock(&history_lock); - if (log_level != -1) { + if (log_level != -1 && entry) { char line[256]; sprint_list_entry(entry, line, sizeof(line)); @@ -745,10 +748,13 @@ static pj_bool_t history_on_rx_msg(pjsip_rx_data *rdata) } ast_mutex_lock(&history_lock); - AST_VECTOR_APPEND(&vector_history, entry); + if (AST_VECTOR_APPEND(&vector_history, entry)) { + ao2_ref(entry, -1); + entry = NULL; + } ast_mutex_unlock(&history_lock); - if (log_level != -1) { + if (log_level != -1 && entry) { char line[256]; sprint_list_entry(entry, line, sizeof(line)); @@ -959,7 +965,9 @@ static int evaluate_history_entry(struct pjsip_history_entry *entry, struct expr /* If this is not an operator, push it to the stack */ if (!it_queue->op) { - AST_VECTOR_APPEND(&stack, it_queue); + if (AST_VECTOR_APPEND(&stack, it_queue)) { + goto error; + } continue; } @@ -1035,7 +1043,11 @@ static int evaluate_history_entry(struct pjsip_history_entry *entry, struct expr if (!result) { goto error; } - AST_VECTOR_APPEND(&stack, result); + if (AST_VECTOR_APPEND(&stack, result)) { + expression_token_free(result); + + goto error; + } } /* @@ -1056,6 +1068,7 @@ static int evaluate_history_entry(struct pjsip_history_entry *entry, struct expr } result = final->result; ast_free(final); + AST_VECTOR_FREE(&stack); return result; @@ -1098,6 +1111,7 @@ static struct vector_history_t *filter_history(struct ast_cli_args *a) queue = build_expression_queue(a); if (!queue) { + AST_VECTOR_PTR_FREE(output); return NULL; } @@ -1118,7 +1132,9 @@ static struct vector_history_t *filter_history(struct ast_cli_args *a) } else if (!res) { continue; } else { - AST_VECTOR_APPEND(output, ao2_bump(entry)); + if (AST_VECTOR_APPEND(output, ao2_bump(entry))) { + ao2_cleanup(entry); + } } } ast_mutex_unlock(&history_lock); diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c index 62b187951..59418e4a9 100644 --- a/res/res_pjsip_pubsub.c +++ b/res/res_pjsip_pubsub.c @@ -938,7 +938,9 @@ static void build_node_children(struct ast_sip_endpoint *endpoint, const struct } ast_debug(2, "Subscription to leaf resource %s resulted in success. Adding to parent %s\n", resource, parent->resource); - AST_VECTOR_APPEND(&parent->children, current); + if (AST_VECTOR_APPEND(&parent->children, current)) { + tree_node_destroy(current); + } } else { ast_debug(2, "Subscription to leaf resource %s resulted in error response %d\n", resource, resp); @@ -953,7 +955,9 @@ static void build_node_children(struct ast_sip_endpoint *endpoint, const struct build_node_children(endpoint, handler, child_list, current, visited); if (AST_VECTOR_SIZE(¤t->children) > 0) { ast_debug(1, "List %s had no successful children.\n", resource); - AST_VECTOR_APPEND(&parent->children, current); + if (AST_VECTOR_APPEND(&parent->children, current)) { + tree_node_destroy(current); + } } else { ast_debug(2, "List %s had successful children. Adding to parent %s\n", resource, parent->resource); @@ -1194,6 +1198,10 @@ static struct ast_sip_subscription *create_virtual_subscriptions(const struct as if (AST_VECTOR_APPEND(&sub->children, child)) { ast_debug(1, "Child subscription to resource %s could not be appended\n", child_node->resource); + destroy_subscription(child); + /* Have to release tree here too because a ref was added + * to child that destroy_subscription() doesn't release. */ + ao2_cleanup(tree); } } @@ -2139,7 +2147,9 @@ static void build_body_part(pj_pool_t *pool, struct ast_sip_subscription *sub, bp->part->body = body; pj_list_insert_before(&bp->part->hdr, bp->cid); - AST_VECTOR_APPEND(parts, bp); + if (AST_VECTOR_APPEND(parts, bp)) { + ast_free(bp); + } } /*! @@ -2200,6 +2210,7 @@ static pjsip_msg_body *generate_list_body(pj_pool_t *pool, struct ast_sip_subscr /* This can happen if issuing partial state and no children of the list have changed state */ if (AST_VECTOR_SIZE(&body_parts) == 0) { + free_body_parts(&body_parts); return NULL; } @@ -2207,6 +2218,7 @@ static pjsip_msg_body *generate_list_body(pj_pool_t *pool, struct ast_sip_subscr rlmi_part = build_rlmi_body(pool, sub, &body_parts, use_full_state); if (!rlmi_part) { + free_body_parts(&body_parts); return NULL; } pjsip_multipart_add_part(pool, multipart, rlmi_part); @@ -2441,9 +2453,14 @@ void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char * void ast_sip_subscription_get_remote_uri(struct ast_sip_subscription *sub, char *buf, size_t size) { pjsip_dialog *dlg; + pjsip_sip_uri *uri; dlg = sub->tree->dlg; - ast_copy_pj_str(buf, &dlg->remote.info_str, size); + uri = pjsip_uri_get_uri(dlg->remote.info->uri); + + if (pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, uri, buf, size) < 0) { + *buf = '\0'; + } } const char *ast_sip_subscription_get_resource_name(struct ast_sip_subscription *sub) @@ -4602,7 +4619,10 @@ static int list_item_handler(const struct aco_option *opt, ast_log(LOG_WARNING, "Ignoring duplicated list item '%s'\n", item); continue; } - if (AST_VECTOR_APPEND(&list->items, ast_strdup(item))) { + + item = ast_strdup(item); + if (!item || AST_VECTOR_APPEND(&list->items, item)) { + ast_free(item); return -1; } } @@ -4738,7 +4758,10 @@ static int populate_list(struct resource_list *list, const char *event, const ch ast_copy_string(list->event, event, sizeof(list->event)); for (i = 0; i < num_resources; ++i) { - if (AST_VECTOR_APPEND(&list->items, ast_strdup(resources[i]))) { + char *resource = ast_strdup(resources[i]); + + if (!resource || AST_VECTOR_APPEND(&list->items, resource)) { + ast_free(resource); return -1; } } diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c index 32906011a..f0da6dee2 100644 --- a/res/res_pjsip_registrar.c +++ b/res/res_pjsip_registrar.c @@ -447,11 +447,19 @@ static void remove_excess_contacts(struct ao2_container *contacts, unsigned int AST_VECTOR_FREE(&contact_vec); } -static int register_aor_core(pjsip_rx_data *rdata, +struct aor_core_response { + /*! Tx data to use for statefull response. NULL for stateless response. */ + pjsip_tx_data *tdata; + /*! SIP response code to send in stateless response */ + int code; +}; + +static void register_aor_core(pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint, struct ast_sip_aor *aor, const char *aor_name, - struct ao2_container *contacts) + struct ao2_container *contacts, + struct aor_core_response *response) { static const pj_str_t USER_AGENT = { "User-Agent", 10 }; @@ -480,19 +488,19 @@ static int register_aor_core(pjsip_rx_data *rdata, if (registrar_validate_contacts(rdata, contacts, aor, &added, &updated, &deleted)) { /* The provided Contact headers do not conform to the specification */ - pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL); ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided"); ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n", ast_sorcery_object_get_id(endpoint)); - return PJ_TRUE; + response->code = 400; + return; } if (registrar_validate_path(rdata, aor, &path_str)) { /* Ensure that intervening proxies did not make invalid modifications to the request */ - pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 420, NULL, NULL, NULL); ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n", ast_sorcery_object_get_id(endpoint)); - return PJ_TRUE; + response->code = 420; + return; } if (aor->remove_existing) { @@ -504,18 +512,18 @@ static int register_aor_core(pjsip_rx_data *rdata, } if (contact_count > aor->max_contacts) { /* Enforce the maximum number of contacts */ - pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts"); ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %u\n", ast_sorcery_object_get_id(endpoint), aor_name, aor->max_contacts); - return PJ_TRUE; + response->code = 403; + return; } details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256); if (!details.pool) { - pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); - return PJ_TRUE; + response->code = 500; + return; } user_agent_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &USER_AGENT, NULL); @@ -730,8 +738,8 @@ static int register_aor_core(pjsip_rx_data *rdata, /* Re-retrieve contacts. Caller will clean up the original container. */ contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor); if (!contacts) { - pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); - return PJ_TRUE; + response->code = 500; + return; } response_contact = ao2_callback(contacts, 0, NULL, NULL); @@ -739,7 +747,8 @@ static int register_aor_core(pjsip_rx_data *rdata, if (ast_sip_create_response(rdata, 200, response_contact, &tdata) != PJ_SUCCESS) { ao2_cleanup(response_contact); ao2_cleanup(contacts); - return PJ_TRUE; + response->code = 500; + return; } ao2_cleanup(response_contact); @@ -754,9 +763,7 @@ static int register_aor_core(pjsip_rx_data *rdata, pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); } - ast_sip_send_stateful_response(rdata, tdata, endpoint); - - return PJ_TRUE; + response->tdata = tdata; } static int register_aor(pjsip_rx_data *rdata, @@ -764,21 +771,32 @@ static int register_aor(pjsip_rx_data *rdata, struct ast_sip_aor *aor, const char *aor_name) { - int res; + struct aor_core_response response = { + .code = 500, + }; struct ao2_container *contacts = NULL; ao2_lock(aor); contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor); if (!contacts) { ao2_unlock(aor); + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), + rdata, response.code, NULL, NULL, NULL); return PJ_TRUE; } - res = register_aor_core(rdata, endpoint, aor, aor_name, contacts); + register_aor_core(rdata, endpoint, aor, aor_name, contacts, &response); ao2_cleanup(contacts); ao2_unlock(aor); - return res; + /* Now send the REGISTER response to the peer */ + if (response.tdata) { + ast_sip_send_stateful_response(rdata, response.tdata, endpoint); + } else { + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), + rdata, response.code, NULL, NULL, NULL); + } + return PJ_TRUE; } static int match_aor(const char *aor_name, const char *id) diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index f174f4781..781d3e4eb 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -174,7 +174,8 @@ void ast_sip_session_unregister_sdp_handler(struct ast_sip_session_sdp_handler * ao2_callback_data(sdp_handlers, OBJ_KEY | OBJ_UNLINK | OBJ_NODATA, remove_handler, (void *)stream_type, handler); } -struct ast_sip_session_media_state *ast_sip_session_media_state_alloc(void) +static struct ast_sip_session_media_state *internal_sip_session_media_state_alloc( + size_t sessions, size_t read_callbacks) { struct ast_sip_session_media_state *media_state; @@ -183,12 +184,12 @@ struct ast_sip_session_media_state *ast_sip_session_media_state_alloc(void) return NULL; } - if (AST_VECTOR_INIT(&media_state->sessions, DEFAULT_NUM_SESSION_MEDIA) < 0) { + if (AST_VECTOR_INIT(&media_state->sessions, sessions) < 0) { ast_free(media_state); return NULL; } - if (AST_VECTOR_INIT(&media_state->read_callbacks, DEFAULT_NUM_SESSION_MEDIA) < 0) { + if (AST_VECTOR_INIT(&media_state->read_callbacks, read_callbacks) < 0) { AST_VECTOR_FREE(&media_state->sessions); ast_free(media_state); return NULL; @@ -197,6 +198,12 @@ struct ast_sip_session_media_state *ast_sip_session_media_state_alloc(void) return media_state; } +struct ast_sip_session_media_state *ast_sip_session_media_state_alloc(void) +{ + return internal_sip_session_media_state_alloc( + DEFAULT_NUM_SESSION_MEDIA, DEFAULT_NUM_SESSION_MEDIA); +} + void ast_sip_session_media_state_reset(struct ast_sip_session_media_state *media_state) { int index; @@ -225,7 +232,9 @@ struct ast_sip_session_media_state *ast_sip_session_media_state_clone(const stru return NULL; } - cloned = ast_sip_session_media_state_alloc(); + cloned = internal_sip_session_media_state_alloc( + AST_VECTOR_SIZE(&media_state->sessions), + AST_VECTOR_SIZE(&media_state->read_callbacks)); if (!cloned) { return NULL; } @@ -452,7 +461,11 @@ struct ast_sip_session_media *ast_sip_session_media_state_add(struct ast_sip_ses } } - AST_VECTOR_REPLACE(&media_state->sessions, position, session_media); + if (AST_VECTOR_REPLACE(&media_state->sessions, position, session_media)) { + ao2_ref(session_media, -1); + + return NULL; + } /* If this stream will be active in some way and it is the first of this type then consider this the default media session to match */ if (!media_state->default_session[type] && ast_stream_get_state(ast_stream_topology_get_stream(media_state->topology, position)) != AST_STREAM_STATE_REMOVED) { @@ -678,7 +691,10 @@ static int handle_incoming_sdp(struct ast_sip_session *session, const pjmedia_sd if (!stream) { return -1; } - ast_stream_topology_set_stream(session->pending_media_state->topology, i, stream); + if (ast_stream_topology_set_stream(session->pending_media_state->topology, i, stream)) { + ast_stream_free(stream); + return -1; + } } session_media = ast_sip_session_media_state_add(session, session->pending_media_state, ast_media_type_from_str(media), i); @@ -1588,7 +1604,11 @@ int ast_sip_session_refresh(struct ast_sip_session *session, } ast_stream_set_state(cloned, AST_STREAM_STATE_REMOVED); - ast_stream_topology_append_stream(media_state->topology, cloned); + if (ast_stream_topology_append_stream(media_state->topology, cloned) < 0) { + ast_stream_free(cloned); + ast_sip_session_media_state_free(media_state); + return -1; + } } /* If the resulting media state matches the existing active state don't bother doing a session refresh */ @@ -1745,7 +1765,10 @@ static int sdp_requires_deferral(struct ast_sip_session *session, const pjmedia_ /* As this is only called on an incoming SDP offer before processing it is not possible * for streams and their media sessions to exist. */ - ast_stream_topology_set_stream(session->pending_media_state->topology, i, stream); + if (ast_stream_topology_set_stream(session->pending_media_state->topology, i, stream)) { + ast_stream_free(stream); + return -1; + } session_media = ast_sip_session_media_state_add(session, session->pending_media_state, ast_media_type_from_str(media), i); if (!session_media) { diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c index 48cbab37b..8f1905f6e 100644 --- a/res/res_pjsip_t38.c +++ b/res/res_pjsip_t38.c @@ -363,7 +363,11 @@ static struct ast_sip_session_media_state *t38_create_media_state(struct ast_sip } ast_stream_set_state(stream, AST_STREAM_STATE_SENDRECV); - ast_stream_topology_set_stream(media_state->topology, 0, stream); + if (ast_stream_topology_set_stream(media_state->topology, 0, stream)) { + ast_stream_free(stream); + ast_sip_session_media_state_free(media_state); + return NULL; + } caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); if (!caps) { @@ -371,9 +375,14 @@ static struct ast_sip_session_media_state *t38_create_media_state(struct ast_sip return NULL; } - ast_format_cap_append(caps, ast_format_t38, 0); ast_stream_set_formats(stream, caps); + /* stream holds a reference to cap, release the local reference + * now so we don't have to deal with it in the error condition. */ ao2_ref(caps, -1); + if (ast_format_cap_append(caps, ast_format_t38, 0)) { + ast_sip_session_media_state_free(media_state); + return NULL; + } session_media = ast_sip_session_media_state_add(session, media_state, AST_MEDIA_TYPE_IMAGE, 0); if (!session_media) { diff --git a/res/res_pjsip_transport_websocket.c b/res/res_pjsip_transport_websocket.c index 22ec19540..3ce90390c 100644 --- a/res/res_pjsip_transport_websocket.c +++ b/res/res_pjsip_transport_websocket.c @@ -203,27 +203,25 @@ static int transport_create(void *data) ast_debug(4, "Creating websocket transport for %s:%s\n", newtransport->transport.type_name, ws_addr_str); + newtransport->transport.info = (char *) pj_pool_alloc(newtransport->transport.pool, + strlen(newtransport->transport.type_name) + strlen(ws_addr_str) + sizeof(" to ")); + sprintf(newtransport->transport.info, "%s to %s", newtransport->transport.type_name, ws_addr_str); + pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ws_addr_str), &newtransport->transport.key.rem_addr); if (newtransport->transport.key.rem_addr.addr.sa_family == pj_AF_INET6()) { newtransport->transport.key.type = transport_type_wss_ipv6; - newtransport->transport.local_name.host.ptr = (char *)pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN); - pj_sockaddr_print(&newtransport->transport.key.rem_addr, newtransport->transport.local_name.host.ptr, PJ_INET6_ADDRSTRLEN, 0); } else { newtransport->transport.key.type = transport_type_wss; - newtransport->transport.local_name.host.ptr = (char *)pj_pool_alloc(pool, PJ_INET_ADDRSTRLEN); - pj_sockaddr_print(&newtransport->transport.key.rem_addr, newtransport->transport.local_name.host.ptr, PJ_INET_ADDRSTRLEN, 0); } newtransport->transport.addr_len = pj_sockaddr_get_len(&newtransport->transport.key.rem_addr); - pj_sockaddr_cp(&newtransport->transport.local_addr, &newtransport->transport.key.rem_addr); - - newtransport->transport.local_name.host.slen = pj_ansi_strlen(newtransport->transport.local_name.host.ptr); - newtransport->transport.local_name.port = pj_sockaddr_get_port(&newtransport->transport.key.rem_addr); + ws_addr_str = ast_sockaddr_stringify(ast_websocket_local_address(newtransport->ws_session)); + pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ws_addr_str), &newtransport->transport.local_addr); + pj_strdup2(pool, &newtransport->transport.local_name.host, ast_sockaddr_stringify_host(ast_websocket_local_address(newtransport->ws_session))); + newtransport->transport.local_name.port = ast_sockaddr_port(ast_websocket_local_address(newtransport->ws_session)); 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; diff --git a/res/res_sorcery_astdb.c b/res/res_sorcery_astdb.c index 8d1633511..8b93b57ba 100644 --- a/res/res_sorcery_astdb.c +++ b/res/res_sorcery_astdb.c @@ -44,6 +44,7 @@ static void *sorcery_astdb_retrieve_fields(const struct ast_sorcery *sorcery, vo static void sorcery_astdb_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const struct ast_variable *fields); static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex); +static void sorcery_astdb_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len); static int sorcery_astdb_update(const struct ast_sorcery *sorcery, void *data, void *object); static int sorcery_astdb_delete(const struct ast_sorcery *sorcery, void *data, void *object); static void sorcery_astdb_close(void *data); @@ -56,6 +57,7 @@ static struct ast_sorcery_wizard astdb_object_wizard = { .retrieve_fields = sorcery_astdb_retrieve_fields, .retrieve_multiple = sorcery_astdb_retrieve_multiple, .retrieve_regex = sorcery_astdb_retrieve_regex, + .retrieve_prefix = sorcery_astdb_retrieve_prefix, .update = sorcery_astdb_update, .delete = sorcery_astdb_delete, .close = sorcery_astdb_close, @@ -327,6 +329,42 @@ static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void regfree(&expression); } +static void sorcery_astdb_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len) +{ + const char *family_prefix = data; + size_t family_len = strlen(family_prefix) + strlen(type) + 1; /* +1 for slash delimiter */ + char family[family_len + 1]; + char tree[prefix_len + sizeof("%")]; + RAII_VAR(struct ast_db_entry *, entries, NULL, ast_db_freetree); + struct ast_db_entry *entry; + + snprintf(tree, sizeof(tree), "%.*s%%", (int) prefix_len, prefix); + snprintf(family, sizeof(family), "%s/%s", family_prefix, type); + + if (!(entries = ast_db_gettree(family, tree))) { + return; + } + + for (entry = entries; entry; entry = entry->next) { + /* The key in the entry includes the family, so we need to strip it out */ + const char *key = entry->key + family_len + 2; + RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); + struct ast_json_error error; + RAII_VAR(void *, object, NULL, ao2_cleanup); + RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy); + + if (!(json = ast_json_load_string(entry->data, &error)) + || (ast_json_to_ast_variables(json, &objset) != AST_JSON_TO_AST_VARS_CODE_SUCCESS) + || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type)) + || !(object = ast_sorcery_alloc(sorcery, type, key)) + || ast_sorcery_objectset_apply(sorcery, object, objset)) { + return; + } + + ao2_link(objects, object); + } +} + static int sorcery_astdb_update(const struct ast_sorcery *sorcery, void *data, void *object) { const char *prefix = data; diff --git a/res/res_sorcery_config.c b/res/res_sorcery_config.c index 0de34c640..20178883b 100644 --- a/res/res_sorcery_config.c +++ b/res/res_sorcery_config.c @@ -71,6 +71,12 @@ struct sorcery_config_fields_cmp_params { /*! \brief Regular expression for checking object id */ regex_t *regex; + /*! \brief Prefix for matching object id */ + const char *prefix; + + /*! \brief Prefix length in bytes for matching object id */ + const size_t prefix_len; + /*! \brief Optional container to put object into */ struct ao2_container *container; }; @@ -83,6 +89,7 @@ static void *sorcery_config_retrieve_fields(const struct ast_sorcery *sorcery, v static void sorcery_config_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const struct ast_variable *fields); static void sorcery_config_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex); +static void sorcery_config_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len); static void sorcery_config_close(void *data); static struct ast_sorcery_wizard config_object_wizard = { @@ -94,6 +101,7 @@ static struct ast_sorcery_wizard config_object_wizard = { .retrieve_fields = sorcery_config_retrieve_fields, .retrieve_multiple = sorcery_config_retrieve_multiple, .retrieve_regex = sorcery_config_retrieve_regex, + .retrieve_prefix = sorcery_config_retrieve_prefix, .close = sorcery_config_close, }; @@ -118,6 +126,11 @@ static int sorcery_config_fields_cmp(void *obj, void *arg, int flags) ao2_link(params->container, obj); } return 0; + } else if (params->prefix) { + if (!strncmp(params->prefix, ast_sorcery_object_get_id(obj), params->prefix_len)) { + ao2_link(params->container, obj); + } + return 0; } else if (params->fields && (!(objset = ast_sorcery_objectset_create(params->sorcery, obj)) || (!ast_variable_lists_match(objset, params->fields, 0)))) { @@ -206,6 +219,24 @@ static void sorcery_config_retrieve_regex(const struct ast_sorcery *sorcery, voi regfree(&expression); } +static void sorcery_config_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len) +{ + struct sorcery_config *config = data; + RAII_VAR(struct ao2_container *, config_objects, ao2_global_obj_ref(config->objects), ao2_cleanup); + struct sorcery_config_fields_cmp_params params = { + .sorcery = sorcery, + .container = objects, + .prefix = prefix, + .prefix_len = prefix_len, + }; + + if (!config_objects) { + return; + } + + ao2_callback(config_objects, OBJ_NODATA | OBJ_MULTIPLE, sorcery_config_fields_cmp, ¶ms); +} + /*! \brief Internal function which determines if criteria has been met for considering an object set applicable */ static int sorcery_is_criteria_met(struct ast_variable *objset, struct ast_variable *criteria) { diff --git a/res/res_sorcery_memory.c b/res/res_sorcery_memory.c index 57d5eace0..6c91dad2d 100644 --- a/res/res_sorcery_memory.c +++ b/res/res_sorcery_memory.c @@ -46,6 +46,7 @@ static void *sorcery_memory_retrieve_fields(const struct ast_sorcery *sorcery, v static void sorcery_memory_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const struct ast_variable *fields); static void sorcery_memory_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex); +static void sorcery_memory_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len); static int sorcery_memory_update(const struct ast_sorcery *sorcery, void *data, void *object); static int sorcery_memory_delete(const struct ast_sorcery *sorcery, void *data, void *object); static void sorcery_memory_close(void *data); @@ -58,6 +59,7 @@ static struct ast_sorcery_wizard memory_object_wizard = { .retrieve_fields = sorcery_memory_retrieve_fields, .retrieve_multiple = sorcery_memory_retrieve_multiple, .retrieve_regex = sorcery_memory_retrieve_regex, + .retrieve_prefix = sorcery_memory_retrieve_prefix, .update = sorcery_memory_update, .delete = sorcery_memory_delete, .close = sorcery_memory_close, @@ -74,6 +76,12 @@ struct sorcery_memory_fields_cmp_params { /*! \brief Regular expression for checking object id */ regex_t *regex; + /*! \brief Prefix for matching object id */ + const char *prefix; + + /*! \brief Prefix length in bytes for matching object id */ + const size_t prefix_len; + /*! \brief Optional container to put object into */ struct ao2_container *container; }; @@ -125,6 +133,11 @@ static int sorcery_memory_fields_cmp(void *obj, void *arg, int flags) ao2_link(params->container, obj); } return 0; + } else if (params->prefix) { + if (!strncmp(params->prefix, ast_sorcery_object_get_id(obj), params->prefix_len)) { + ao2_link(params->container, obj); + } + return 0; } else if (params->fields && (!(objset = ast_sorcery_objectset_create(params->sorcery, obj)) || (!ast_variable_lists_match(objset, params->fields, 0)))) { @@ -198,6 +211,18 @@ static void sorcery_memory_retrieve_regex(const struct ast_sorcery *sorcery, voi regfree(&expression); } +static void sorcery_memory_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *prefix, const size_t prefix_len) +{ + struct sorcery_memory_fields_cmp_params params = { + .sorcery = sorcery, + .container = objects, + .prefix = prefix, + .prefix_len = prefix_len, + }; + + ao2_callback(data, 0, sorcery_memory_fields_cmp, ¶ms); +} + static int sorcery_memory_update(const struct ast_sorcery *sorcery, void *data, void *object) { RAII_VAR(void *, existing, NULL, ao2_cleanup); diff --git a/res/res_sorcery_memory_cache.c b/res/res_sorcery_memory_cache.c index bf2347ccd..30e6ef04b 100644 --- a/res/res_sorcery_memory_cache.c +++ b/res/res_sorcery_memory_cache.c @@ -185,6 +185,10 @@ struct sorcery_memory_cache_fields_cmp_params { const struct ast_variable *fields; /*! \brief Regular expression for checking object id */ regex_t *regex; + /*! \brief Prefix for matching object id */ + const char *prefix; + /*! \brief Prefix length in bytes for matching object id */ + const size_t prefix_len; /*! \brief Optional container to put object into */ struct ao2_container *container; }; @@ -201,6 +205,8 @@ static void sorcery_memory_cache_retrieve_multiple(const struct ast_sorcery *sor struct ao2_container *objects, const struct ast_variable *fields); static void sorcery_memory_cache_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex); +static void sorcery_memory_cache_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, + struct ao2_container *objects, const char *prefix, const size_t prefix_len); static int sorcery_memory_cache_delete(const struct ast_sorcery *sorcery, void *data, void *object); static void sorcery_memory_cache_close(void *data); @@ -216,6 +222,7 @@ static struct ast_sorcery_wizard memory_cache_object_wizard = { .retrieve_fields = sorcery_memory_cache_retrieve_fields, .retrieve_multiple = sorcery_memory_cache_retrieve_multiple, .retrieve_regex = sorcery_memory_cache_retrieve_regex, + .retrieve_prefix = sorcery_memory_cache_retrieve_prefix, .close = sorcery_memory_cache_close, }; @@ -1253,6 +1260,11 @@ static int sorcery_memory_cache_fields_cmp(void *obj, void *arg, int flags) ao2_link(params->container, cached->object); } return 0; + } else if (params->prefix) { + if (!strncmp(params->prefix, ast_sorcery_object_get_id(cached->object), params->prefix_len)) { + ao2_link(params->container, cached->object); + } + return 0; } else if (params->fields && (!ast_variable_lists_match(cached->objectset, params->fields, 0))) { /* If we can't turn the object into an object set OR if differences exist between the fields @@ -1378,6 +1390,40 @@ static void sorcery_memory_cache_retrieve_regex(const struct ast_sorcery *sorcer /*! * \internal + * \brief Callback function to retrieve multiple objects whose id matches a prefix + * + * \param sorcery The sorcery instance + * \param data The sorcery memory cache + * \param type The type of the object to retrieve + * \param objects Container to place the objects into + * \param prefix Prefix to match against the object id + */ +static void sorcery_memory_cache_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, + struct ao2_container *objects, const char *prefix, const size_t prefix_len) +{ + struct sorcery_memory_cache *cache = data; + struct sorcery_memory_cache_fields_cmp_params params = { + .sorcery = sorcery, + .cache = cache, + .container = objects, + .prefix = prefix, + .prefix_len = prefix_len, + }; + + if (is_passthru_update() || !cache->full_backend_cache) { + return; + } + + memory_cache_full_update(sorcery, type, cache); + ao2_callback(cache->objects, 0, sorcery_memory_cache_fields_cmp, ¶ms); + + if (ao2_container_count(objects)) { + memory_cache_stale_check(sorcery, cache); + } +} + +/*! + * \internal * \brief Callback function to finish configuring the memory cache * * \param data The sorcery memory cache diff --git a/res/res_sorcery_realtime.c b/res/res_sorcery_realtime.c index 3f114046c..a858cbcef 100644 --- a/res/res_sorcery_realtime.c +++ b/res/res_sorcery_realtime.c @@ -57,6 +57,8 @@ static void *sorcery_realtime_retrieve_fields(const struct ast_sorcery *sorcery, static void sorcery_realtime_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const struct ast_variable *fields); static void sorcery_realtime_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex); +static void sorcery_realtime_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, + struct ao2_container *objects, const char *prefix, const size_t prefix_len); static int sorcery_realtime_update(const struct ast_sorcery *sorcery, void *data, void *object); static int sorcery_realtime_delete(const struct ast_sorcery *sorcery, void *data, void *object); static void sorcery_realtime_close(void *data); @@ -69,6 +71,7 @@ static struct ast_sorcery_wizard realtime_object_wizard = { .retrieve_fields = sorcery_realtime_retrieve_fields, .retrieve_multiple = sorcery_realtime_retrieve_multiple, .retrieve_regex = sorcery_realtime_retrieve_regex, + .retrieve_prefix = sorcery_realtime_retrieve_prefix, .update = sorcery_realtime_update, .delete = sorcery_realtime_delete, .close = sorcery_realtime_close, @@ -260,6 +263,23 @@ static void sorcery_realtime_retrieve_regex(const struct ast_sorcery *sorcery, v sorcery_realtime_retrieve_multiple(sorcery, data, type, objects, fields); } +static void sorcery_realtime_retrieve_prefix(const struct ast_sorcery *sorcery, void *data, const char *type, + struct ao2_container *objects, const char *prefix, const size_t prefix_len) +{ + char field[strlen(UUID_FIELD) + 6], value[prefix_len + 2]; + RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy); + + if (prefix_len) { + snprintf(field, sizeof(field), "%s LIKE", UUID_FIELD); + snprintf(value, sizeof(value), "%.*s%%", (int) prefix_len, prefix); + if (!(fields = ast_variable_new(field, value, ""))) { + return; + } + } + + sorcery_realtime_retrieve_multiple(sorcery, data, type, objects, fields); +} + static int sorcery_realtime_update(const struct ast_sorcery *sorcery, void *data, void *object) { struct sorcery_config *config = data; diff --git a/third-party/pjproject/patches/0010-r5665-svn-backport-ICE-Use-STUN-FINGERPRINT-attribut.patch b/third-party/pjproject/patches/0010-r5665-svn-backport-ICE-Use-STUN-FINGERPRINT-attribut.patch deleted file mode 100644 index 3c83cc502..000000000 --- a/third-party/pjproject/patches/0010-r5665-svn-backport-ICE-Use-STUN-FINGERPRINT-attribut.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 06bc834969a34d2c60e05beed3051b4a6d34c125 Mon Sep 17 00:00:00 2001 -From: Richard Mudgett <rmudgett@digium.com> -Date: Thu, 28 Sep 2017 10:57:23 -0500 -Subject: [PATCH 3/3] r5665 svn backport ICE: Use STUN FINGERPRINT attribute - when sending keepalives - -Fixed #2046: ICE: Use STUN FINGERPRINT attribute when sending keepalives - -Per RFC 5245 Section 10: - - If STUN is being used for keepalives, a STUN Binding Indication is - used [RFC5389]. The Indication MUST NOT utilize any authentication - mechanism. It SHOULD contain the FINGERPRINT attribute to aid in - demultiplexing, but SHOULD NOT contain any other attributes. ---- - pjnath/src/pjnath/ice_session.c | 8 +++++--- - 1 file changed, 5 insertions(+), 3 deletions(-) - -diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c -index 27a2950..63a0d1c 100644 ---- a/pjnath/src/pjnath/ice_session.c -+++ b/pjnath/src/pjnath/ice_session.c -@@ -1217,10 +1217,12 @@ static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now) - msg_data = PJ_POOL_ZALLOC_T(tdata->pool, pj_ice_msg_data); - msg_data->transport_id = the_check->lcand->transport_id; - -- /* Temporarily disable FINGERPRINT. The Binding Indication -- * SHOULD NOT contain any attributes. -+ /* RFC 5245 Section 10: -+ * The Binding Indication SHOULD contain the FINGERPRINT attribute -+ * to aid in demultiplexing, but SHOULD NOT contain any other -+ * attributes. - */ -- saved = pj_stun_session_use_fingerprint(comp->stun_sess, PJ_FALSE); -+ saved = pj_stun_session_use_fingerprint(comp->stun_sess, PJ_TRUE); - - /* Send to session */ - addr_len = pj_sockaddr_get_len(&the_check->rcand->addr); --- -2.7.4 - diff --git a/third-party/pjproject/patches/0020-sip_parser-Add-validity-checking-for-numeric-header-.patch b/third-party/pjproject/patches/0020-sip_parser-Add-validity-checking-for-numeric-header-.patch deleted file mode 100644 index dfee4b2c2..000000000 --- a/third-party/pjproject/patches/0020-sip_parser-Add-validity-checking-for-numeric-header-.patch +++ /dev/null @@ -1,973 +0,0 @@ -From b21042956dc4d3526052d5030953e5c565bb0895 Mon Sep 17 00:00:00 2001 -From: George Joseph <gjoseph@digium.com> -Date: Thu, 2 Nov 2017 08:23:00 -0600 -Subject: [PATCH] sip_parser: Add validity checking for numeric header values - -Parsing the numeric header fields like cseq, ttl, port, etc. all -had the potential to overflow, either causing unintended values to -be captured or, if the values were subsequently converted back to -strings, a buffer overrun. To address this, new "strto" functions -have been created that do range checking and those functions are -used wherever possible in the parser. - - * Created pjlib/include/limits.h and pjlib/include/compat/limits.h - to either include the system limits.h or define common numeric - limits if there is no system limits.h. - - * Created strto*_validate functions in sip_parser that take bounds - and on failure call the on_str_parse_error function which prints - an error message and calls PJ_THROW. - - * Updated sip_parser to validate the numeric fields. - - * Fixed an issue in sip_transport that prevented error messages - from being properly displayed. - - * Added "volatile" to some variables referenced in PJ_CATCH blocks - as the optimizer was sometimes optimizing them away. - - * Fixed length calculation in sip_transaction/create_tsx_key_2543 - to account for signed ints being 11 characters, not 9. - -Reported by: Youngsung Kim at LINE Corporation ---- - pjlib/build/pjlib.vcproj | 14 +++- - pjlib/build/pjlib.vcxproj | 4 +- - pjlib/build/pjlib.vcxproj.filters | 6 ++ - pjlib/include/pj/compat/limits.h | 65 +++++++++++++++ - pjlib/include/pj/compat/os_win32.h | 1 + - pjlib/include/pj/limits.h | 51 ++++++++++++ - pjlib/include/pj/string.h | 46 +++++++++- - pjlib/include/pj/types.h | 3 - - pjlib/src/pj/string.c | 119 +++++++++++++++++++++++++- - pjlib/src/pj/timer.c | 1 + - pjsip/include/pjsip/sip_parser.h | 25 ++++++ - pjsip/src/pjsip/sip_parser.c | 166 +++++++++++++++++++++++++++++-------- - pjsip/src/pjsip/sip_transaction.c | 4 +- - pjsip/src/pjsip/sip_transport.c | 7 +- - 14 files changed, 463 insertions(+), 49 deletions(-) - create mode 100644 pjlib/include/pj/compat/limits.h - create mode 100644 pjlib/include/pj/limits.h - -diff --git a/pjlib/build/pjlib.vcproj b/pjlib/build/pjlib.vcproj -index 6a217a0b7..12592ef94 100644 ---- a/pjlib/build/pjlib.vcproj -+++ b/pjlib/build/pjlib.vcproj -@@ -14967,7 +14967,11 @@ - </File>
- <File
- RelativePath="..\include\pj\ip_helper.h"
-- >
-+ > -+ </File> -+ <File -+ RelativePath="..\include\pj\limits.h" -+ > - </File>
- <File
- RelativePath="..\include\pj\list.h"
-@@ -15070,8 +15074,12 @@ - </File>
- <File
- RelativePath="..\include\pj\compat\high_precision.h"
-- >
-- </File>
-+ > -+ </File> -+ <File -+ RelativePath="..\include\pj\compat\limits.h" -+ > -+ </File> - <File
- RelativePath="..\include\pj\compat\m_alpha.h"
- >
-diff --git a/pjlib/build/pjlib.vcxproj b/pjlib/build/pjlib.vcxproj -index abf09ec44..e41731e3c 100644 ---- a/pjlib/build/pjlib.vcxproj -+++ b/pjlib/build/pjlib.vcxproj -@@ -494,7 +494,7 @@ - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> - </ClCompile> -- <ClCompile Include="..\src\pj\file_io_win32.c" /> -+ <ClCompile Include="..\src\pj\file_io_win32.c" /> - <ClCompile Include="..\src\pj\guid.c" /> - <ClCompile Include="..\src\pj\guid_simple.c"> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-Dynamic|Win32'">true</ExcludedFromBuild> -@@ -890,6 +890,7 @@ - <ClInclude Include="..\include\pj\compat\ctype.h" /> - <ClInclude Include="..\include\pj\compat\errno.h" /> - <ClInclude Include="..\include\pj\compat\high_precision.h" /> -+ <ClInclude Include="..\include\pj\compat\limits.h" /> - <ClInclude Include="..\include\pj\compat\malloc.h" /> - <ClInclude Include="..\include\pj\compat\m_alpha.h" /> - <ClInclude Include="..\include\pj\compat\m_i386.h" /> -@@ -925,6 +926,7 @@ - <ClInclude Include="..\include\pj\hash.h" /> - <ClInclude Include="..\include\pj\ioqueue.h" /> - <ClInclude Include="..\include\pj\ip_helper.h" /> -+ <ClInclude Include="..\include\pj\limits.h" /> - <ClInclude Include="..\include\pj\list.h" /> - <ClInclude Include="..\include\pj\list_i.h" /> - <ClInclude Include="..\include\pj\lock.h" /> -diff --git a/pjlib/build/pjlib.vcxproj.filters b/pjlib/build/pjlib.vcxproj.filters -index 0b5cbf109..6f343b019 100644 ---- a/pjlib/build/pjlib.vcxproj.filters -+++ b/pjlib/build/pjlib.vcxproj.filters -@@ -439,5 +439,11 @@ - <ClInclude Include="..\include\pj\compat\os_winuwp.h"> - <Filter>Header Files\compat</Filter> - </ClInclude> -+ <ClInclude Include="..\include\pj\limits.h"> -+ <Filter>Header Files</Filter> -+ </ClInclude> -+ <ClInclude Include="..\include\pj\compat\limits.h"> -+ <Filter>Header Files\compat</Filter> -+ </ClInclude> - </ItemGroup> - </Project> -\ No newline at end of file -diff --git a/pjlib/include/pj/compat/limits.h b/pjlib/include/pj/compat/limits.h -new file mode 100644 -index 000000000..fba0625df ---- /dev/null -+++ b/pjlib/include/pj/compat/limits.h -@@ -0,0 +1,65 @@ -+/* $Id$ */ -+/* -+ * Copyright (C) 2017 Teluu Inc. (http://www.teluu.com) -+ * Copyright (C) 2017 George Joseph <gjoseph@digium.com> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#ifndef __PJ_COMPAT_LIMITS_H__ -+#define __PJ_COMPAT_LIMITS_H__ -+ -+/** -+ * @file limits.h -+ * @brief Provides integer limits normally found in limits.h. -+ */ -+ -+#if defined(PJ_HAS_LIMITS_H) && PJ_HAS_LIMITS_H != 0 -+# include <limits.h> -+#else -+ -+# ifdef _MSC_VER -+# pragma message("limits.h is not found or not supported. LONG_MIN and "\ -+ "LONG_MAX will be defined by the library in "\ -+ "pj/compats/limits.h and overridable in config_site.h") -+# else -+# warning "limits.h is not found or not supported. LONG_MIN and LONG_MAX " \ -+ "will be defined by the library in pj/compats/limits.h and "\ -+ "overridable in config_site.h" -+# endif -+ -+/* Minimum and maximum values a `signed long int' can hold. */ -+# ifndef LONG_MAX -+# if __WORDSIZE == 64 -+# define LONG_MAX 9223372036854775807L -+# else -+# define LONG_MAX 2147483647L -+# endif -+# endif -+ -+# ifndef LONG_MIN -+# define LONG_MIN (-LONG_MAX - 1L) -+# endif -+ -+/* Maximum value an `unsigned long int' can hold. (Minimum is 0.) */ -+# ifndef ULONG_MAX -+# if __WORDSIZE == 64 -+# define ULONG_MAX 18446744073709551615UL -+# else -+# define ULONG_MAX 4294967295UL -+# endif -+# endif -+#endif -+ -+#endif /* __PJ_COMPAT_LIMITS_H__ */ -diff --git a/pjlib/include/pj/compat/os_win32.h b/pjlib/include/pj/compat/os_win32.h -index 4fa8b21ea..9b18e4eb1 100644 ---- a/pjlib/include/pj/compat/os_win32.h -+++ b/pjlib/include/pj/compat/os_win32.h -@@ -57,6 +57,7 @@ - #define PJ_HAS_SYS_TYPES_H 1 - #define PJ_HAS_TIME_H 1 - #define PJ_HAS_UNISTD_H 0 -+#define PJ_HAS_LIMITS_H 1 - - #define PJ_HAS_MSWSOCK_H 1 - #define PJ_HAS_WINSOCK_H 0 -diff --git a/pjlib/include/pj/limits.h b/pjlib/include/pj/limits.h -new file mode 100644 -index 000000000..8b00ae52a ---- /dev/null -+++ b/pjlib/include/pj/limits.h -@@ -0,0 +1,51 @@ -+/* $Id$ */ -+/* -+ * Copyright (C) 2017 Teluu Inc. (http://www.teluu.com) -+ * Copyright (C) 2017 George Joseph <gjoseph@digium.com> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#ifndef __PJ_LIMITS_H__ -+#define __PJ_LIMITS_H__ -+ -+/** -+ * @file limits.h -+ * @brief Common min and max values -+ */ -+ -+#include <pj/compat/limits.h> -+ -+/** Maximum value for signed 32-bit integer. */ -+#define PJ_MAXINT32 0x7fffffff -+ -+/** Minimum value for signed 32-bit integer. */ -+#define PJ_MININT32 0x80000000 -+ -+/** Maximum value for unsigned 16-bit integer. */ -+#define PJ_MAXUINT16 0xffff -+ -+/** Maximum value for unsigned char. */ -+#define PJ_MAXUINT8 0xff -+ -+/** Maximum value for long. */ -+#define PJ_MAXLONG LONG_MAX -+ -+/** Minimum value for long. */ -+#define PJ_MINLONG LONG_MIN -+ -+/** Minimum value for unsigned long. */ -+#define PJ_MAXULONG ULONG_MAX -+ -+#endif /* __PJ_LIMITS_H__ */ -diff --git a/pjlib/include/pj/string.h b/pjlib/include/pj/string.h -index 70a1d6c8c..5de236a65 100644 ---- a/pjlib/include/pj/string.h -+++ b/pjlib/include/pj/string.h -@@ -28,7 +28,6 @@ - #include <pj/types.h> - #include <pj/compat/string.h> - -- - PJ_BEGIN_DECL - - /** -@@ -636,6 +635,29 @@ PJ_DECL(char*) pj_create_random_string(char *str, pj_size_t length); - PJ_DECL(long) pj_strtol(const pj_str_t *str); - - /** -+ * Convert string to signed long integer. The conversion will stop as -+ * soon as non-digit character is found or all the characters have -+ * been processed. -+ * -+ * @param str the string. -+ * @param value Pointer to a long to receive the value. -+ * -+ * @return PJ_SUCCESS if successful. Otherwise: -+ * PJ_ETOOSMALL if the value was an impossibly long negative number. -+ * In this case *value will be set to LONG_MIN. -+ * \n -+ * PJ_ETOOBIG if the value was an impossibly long positive number. -+ * In this case, *value will be set to LONG_MAX. -+ * \n -+ * PJ_EINVAL if the input string was NULL, the value pointer was NULL -+ * or the input string could not be parsed at all such as starting with -+ * a character other than a '+', '-' or not in the '0' - '9' range. -+ * In this case, *value will be left untouched. -+ */ -+PJ_DECL(pj_status_t) pj_strtol2(const pj_str_t *str, long *value); -+ -+ -+/** - * Convert string to unsigned integer. The conversion will stop as - * soon as non-digit character is found or all the characters have - * been processed. -@@ -664,6 +686,27 @@ PJ_DECL(unsigned long) pj_strtoul2(const pj_str_t *str, pj_str_t *endptr, - unsigned base); - - /** -+ * Convert string to unsigned long integer. The conversion will stop as -+ * soon as non-digit character is found or all the characters have -+ * been processed. -+ * -+ * @param str The input string. -+ * @param value Pointer to an unsigned long to receive the value. -+ * @param base Number base to use. -+ * -+ * @return PJ_SUCCESS if successful. Otherwise: -+ * PJ_ETOOBIG if the value was an impossibly long positive number. -+ * In this case, *value will be set to ULONG_MAX. -+ * \n -+ * PJ_EINVAL if the input string was NULL, the value pointer was NULL -+ * or the input string could not be parsed at all such as starting -+ * with a character outside the base character range. In this case, -+ * *value will be left untouched. -+ */ -+PJ_DECL(pj_status_t) pj_strtoul3(const pj_str_t *str, unsigned long *value, -+ unsigned base); -+ -+/** - * Convert string to float. - * - * @param str the string. -@@ -786,7 +829,6 @@ PJ_INLINE(void*) pj_memchr(const void *buf, int c, pj_size_t size) - return (void*)memchr((void*)buf, c, size); - } - -- - /** - * @} - */ -diff --git a/pjlib/include/pj/types.h b/pjlib/include/pj/types.h -index 0e0e2d9a7..8c9f78238 100644 ---- a/pjlib/include/pj/types.h -+++ b/pjlib/include/pj/types.h -@@ -280,9 +280,6 @@ typedef int pj_exception_id_t; - /** Utility macro to compute the number of elements in static array. */ - #define PJ_ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) - --/** Maximum value for signed 32-bit integer. */ --#define PJ_MAXINT32 0x7FFFFFFFL -- - /** - * Length of object names. - */ -diff --git a/pjlib/src/pj/string.c b/pjlib/src/pj/string.c -index 307cfb47e..b95f141be 100644 ---- a/pjlib/src/pj/string.c -+++ b/pjlib/src/pj/string.c -@@ -23,11 +23,14 @@ - #include <pj/ctype.h> - #include <pj/rand.h> - #include <pj/os.h> -+#include <pj/errno.h> -+#include <pj/limits.h> - - #if PJ_FUNCTIONS_ARE_INLINED==0 - # include <pj/string_i.h> - #endif - -+ - PJ_DEF(pj_ssize_t) pj_strspn(const pj_str_t *str, const pj_str_t *set_char) - { - pj_ssize_t i, j, count = 0; -@@ -230,6 +233,55 @@ PJ_DEF(long) pj_strtol(const pj_str_t *str) - return pj_strtoul(str); - } - -+ -+PJ_DEF(pj_status_t) pj_strtol2(const pj_str_t *str, long *value) -+{ -+ pj_str_t s; -+ unsigned long retval = 0; -+ pj_bool_t is_negative = PJ_FALSE; -+ int rc = 0; -+ -+ PJ_CHECK_STACK(); -+ -+ if (!str || !value) { -+ return PJ_EINVAL; -+ } -+ -+ s = *str; -+ pj_strltrim(&s); -+ -+ if (s.slen == 0) -+ return PJ_EINVAL; -+ -+ if (s.ptr[0] == '+' || s.ptr[0] == '-') { -+ is_negative = (s.ptr[0] == '-'); -+ s.ptr += 1; -+ s.slen -= 1; -+ } -+ -+ rc = pj_strtoul3(&s, &retval, 10); -+ if (rc == PJ_EINVAL) { -+ return rc; -+ } else if (rc != PJ_SUCCESS) { -+ *value = is_negative ? PJ_MINLONG : PJ_MAXLONG; -+ return is_negative ? PJ_ETOOSMALL : PJ_ETOOBIG; -+ } -+ -+ if (retval > PJ_MAXLONG && !is_negative) { -+ *value = PJ_MAXLONG; -+ return PJ_ETOOBIG; -+ } -+ -+ if (retval > (PJ_MAXLONG + 1UL) && is_negative) { -+ *value = PJ_MINLONG; -+ return PJ_ETOOSMALL; -+ } -+ -+ *value = is_negative ? -(long)retval : retval; -+ -+ return PJ_SUCCESS; -+} -+ - PJ_DEF(unsigned long) pj_strtoul(const pj_str_t *str) - { - unsigned long value; -@@ -282,6 +334,71 @@ PJ_DEF(unsigned long) pj_strtoul2(const pj_str_t *str, pj_str_t *endptr, - return value; - } - -+PJ_DEF(pj_status_t) pj_strtoul3(const pj_str_t *str, unsigned long *value, -+ unsigned base) -+{ -+ pj_str_t s; -+ unsigned i; -+ -+ PJ_CHECK_STACK(); -+ -+ if (!str || !value) { -+ return PJ_EINVAL; -+ } -+ -+ s = *str; -+ pj_strltrim(&s); -+ -+ if (s.slen == 0 || s.ptr[0] < '0' || -+ (base <= 10 && (unsigned)s.ptr[0] > ('0' - 1) + base) || -+ (base == 16 && !pj_isxdigit(s.ptr[0]))) -+ { -+ return PJ_EINVAL; -+ } -+ -+ *value = 0; -+ if (base <= 10) { -+ for (i=0; i<(unsigned)s.slen; ++i) { -+ unsigned c = s.ptr[i] - '0'; -+ if (s.ptr[i] < '0' || (unsigned)s.ptr[i] > ('0' - 1) + base) { -+ break; -+ } -+ if (*value > PJ_MAXULONG / base) { -+ *value = PJ_MAXULONG; -+ return PJ_ETOOBIG; -+ } -+ -+ *value *= base; -+ if ((PJ_MAXULONG - *value) < c) { -+ *value = PJ_MAXULONG; -+ return PJ_ETOOBIG; -+ } -+ *value += c; -+ } -+ } else if (base == 16) { -+ for (i=0; i<(unsigned)s.slen; ++i) { -+ unsigned c = pj_hex_digit_to_val(s.ptr[i]); -+ if (!pj_isxdigit(s.ptr[i])) -+ break; -+ -+ if (*value > PJ_MAXULONG / base) { -+ *value = PJ_MAXULONG; -+ return PJ_ETOOBIG; -+ } -+ *value *= base; -+ if ((PJ_MAXULONG - *value) < c) { -+ *value = PJ_MAXULONG; -+ return PJ_ETOOBIG; -+ } -+ *value += c; -+ } -+ } else { -+ pj_assert(!"Unsupported base"); -+ return PJ_EINVAL; -+ } -+ return PJ_SUCCESS; -+} -+ - PJ_DEF(float) pj_strtof(const pj_str_t *str) - { - pj_str_t part; -@@ -356,5 +473,3 @@ PJ_DEF(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad) - - return len; - } -- -- -diff --git a/pjlib/src/pj/timer.c b/pjlib/src/pj/timer.c -index 225be4498..399e114a8 100644 ---- a/pjlib/src/pj/timer.c -+++ b/pjlib/src/pj/timer.c -@@ -36,6 +36,7 @@ - #include <pj/lock.h> - #include <pj/log.h> - #include <pj/rand.h> -+#include <pj/limits.h> - - #define THIS_FILE "timer.c" - -diff --git a/pjsip/include/pjsip/sip_parser.h b/pjsip/include/pjsip/sip_parser.h -index 0d767f0ad..5691fed3a 100644 ---- a/pjsip/include/pjsip/sip_parser.h -+++ b/pjsip/include/pjsip/sip_parser.h -@@ -39,6 +39,26 @@ PJ_BEGIN_DECL - */ - - /** -+ * Contants for limit checks -+ */ -+#define PJSIP_MIN_CONTENT_LENGTH 0 -+#define PJSIP_MAX_CONTENT_LENGTH PJ_MAXINT32 -+#define PJSIP_MIN_PORT 0 -+#define PJSIP_MAX_PORT PJ_MAXUINT16 -+#define PJSIP_MIN_TTL 0 -+#define PJSIP_MAX_TTL PJ_MAXUINT8 -+#define PJSIP_MIN_STATUS_CODE 100 -+#define PJSIP_MAX_STATUS_CODE 999 -+#define PJSIP_MIN_Q1000 0 -+#define PJSIP_MAX_Q1000 PJ_MAXINT32 / 1000 -+#define PJSIP_MIN_EXPIRES 0 -+#define PJSIP_MAX_EXPIRES PJ_MAXINT32 -+#define PJSIP_MIN_CSEQ 0 -+#define PJSIP_MAX_CSEQ PJ_MAXINT32 -+#define PJSIP_MIN_RETRY_AFTER 0 -+#define PJSIP_MAX_RETRY_AFTER PJ_MAXINT32 -+ -+/** - * URI Parsing options. - */ - enum -@@ -64,6 +84,11 @@ enum - extern int PJSIP_SYN_ERR_EXCEPTION; - - /** -+ * Invalid value error exception value. -+ */ -+extern int PJSIP_EINVAL_ERR_EXCEPTION; -+ -+/** - * This structure is used to get error reporting from parser. - */ - typedef struct pjsip_parser_err_report -diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c -index cf3b879f6..f9a0e65b5 100644 ---- a/pjsip/src/pjsip/sip_parser.c -+++ b/pjsip/src/pjsip/sip_parser.c -@@ -34,6 +34,7 @@ - #include <pj/string.h> - #include <pj/ctype.h> - #include <pj/assert.h> -+#include <pj/limits.h> - - #define THIS_FILE "sip_parser.c" - -@@ -93,6 +94,7 @@ static unsigned uri_handler_count; - * Global vars (also extern). - */ - int PJSIP_SYN_ERR_EXCEPTION = -1; -+int PJSIP_EINVAL_ERR_EXCEPTION = -2; - - /* Parser constants */ - static pjsip_parser_const_t pconst = -@@ -205,7 +207,6 @@ static unsigned long pj_strtoul_mindigit(const pj_str_t *str, - /* Case insensitive comparison */ - #define parser_stricmp(s1, s2) (s1.slen!=s2.slen || pj_stricmp_alnum(&s1, &s2)) - -- - /* Get a token and unescape */ - PJ_INLINE(void) parser_get_and_unescape(pj_scanner *scanner, pj_pool_t *pool, - const pj_cis_t *spec, -@@ -223,8 +224,6 @@ PJ_INLINE(void) parser_get_and_unescape(pj_scanner *scanner, pj_pool_t *pool, - #endif - } - -- -- - /* Syntax error handler for parser. */ - static void on_syntax_error(pj_scanner *scanner) - { -@@ -232,6 +231,60 @@ static void on_syntax_error(pj_scanner *scanner) - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - } - -+/* Syntax error handler for parser. */ -+static void on_str_parse_error(const pj_str_t *str, int rc) -+{ -+ char *s; -+ -+ switch(rc) { -+ case PJ_EINVAL: -+ s = "NULL input string, invalid input string, or NULL return "\ -+ "value pointer"; -+ break; -+ case PJ_ETOOSMALL: -+ s = "String value was less than the minimum allowed value."; -+ break; -+ case PJ_ETOOBIG: -+ s = "String value was greater than the maximum allowed value."; -+ break; -+ default: -+ s = "Unknown error"; -+ } -+ -+ if (str) { -+ PJ_LOG(1, (THIS_FILE, "Error parsing '%.*s': %s", -+ (int)str->slen, str->ptr, s)); -+ } else { -+ PJ_LOG(1, (THIS_FILE, "Can't parse input string: %s", s)); -+ } -+ PJ_THROW(PJSIP_EINVAL_ERR_EXCEPTION); -+} -+ -+static void strtoi_validate(const pj_str_t *str, int min_val, -+ int max_val, int *value) -+{ -+ long retval; -+ pj_status_t status; -+ -+ if (!str || !value) { -+ on_str_parse_error(str, PJ_EINVAL); -+ } -+ status = pj_strtol2(str, &retval); -+ if (status != PJ_EINVAL) { -+ if (min_val > retval) { -+ *value = min_val; -+ status = PJ_ETOOSMALL; -+ } else if (retval > max_val) { -+ *value = max_val; -+ status = PJ_ETOOBIG; -+ } else -+ *value = (int)retval; -+ } -+ -+ if (status != PJ_SUCCESS) -+ on_str_parse_error(str, status); -+} -+ - /* Get parser constants. */ - PJ_DEF(const pjsip_parser_const_t*) pjsip_parser_const(void) - { -@@ -285,6 +338,14 @@ static pj_status_t init_parser() - PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); - - /* -+ * Invalid value exception. -+ */ -+ pj_assert (PJSIP_EINVAL_ERR_EXCEPTION == -2); -+ status = pj_exception_id_alloc("PJSIP invalid value error", -+ &PJSIP_EINVAL_ERR_EXCEPTION); -+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); -+ -+ /* - * Init character input spec (cis) - */ - -@@ -502,6 +563,9 @@ void deinit_sip_parser(void) - /* Deregister exception ID */ - pj_exception_id_free(PJSIP_SYN_ERR_EXCEPTION); - PJSIP_SYN_ERR_EXCEPTION = -1; -+ -+ pj_exception_id_free(PJSIP_EINVAL_ERR_EXCEPTION); -+ PJSIP_EINVAL_ERR_EXCEPTION = -2; - } - pj_leave_critical_section(); - } -@@ -766,7 +830,7 @@ PJ_DEF(pjsip_msg *) pjsip_parse_rdata( char *buf, pj_size_t size, - } - - /* Determine if a message has been received. */ --PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size, -+PJ_DEF(pj_status_t) pjsip_find_msg( const char *buf, pj_size_t size, - pj_bool_t is_datagram, pj_size_t *msg_size) - { - #if PJ_HAS_TCP -@@ -776,6 +840,7 @@ PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size, - const char *line; - int content_length = -1; - pj_str_t cur_msg; -+ pj_status_t status = PJ_SUCCESS; - const pj_str_t end_hdr = { "\n\r\n", 3}; - - *msg_size = size; -@@ -836,9 +901,16 @@ PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size, - pj_scan_get_newline(&scanner); - - /* Found a valid Content-Length header. */ -- content_length = pj_strtoul(&str_clen); -+ strtoi_validate(&str_clen, PJSIP_MIN_CONTENT_LENGTH, -+ PJSIP_MAX_CONTENT_LENGTH, &content_length); - } - PJ_CATCH_ANY { -+ int eid = PJ_GET_EXCEPTION(); -+ if (eid == PJSIP_SYN_ERR_EXCEPTION) { -+ status = PJSIP_EMISSINGHDR; -+ } else if (eid == PJSIP_EINVAL_ERR_EXCEPTION) { -+ status = PJSIP_EINVALIDHDR; -+ } - content_length = -1; - } - PJ_END -@@ -858,7 +930,7 @@ PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size, - - /* Found Content-Length? */ - if (content_length == -1) { -- return PJSIP_EMISSINGHDR; -+ return status; - } - - /* Enough packet received? */ -@@ -938,10 +1010,14 @@ static pj_bool_t is_next_sip_version(pj_scanner *scanner) - static pjsip_msg *int_parse_msg( pjsip_parse_ctx *ctx, - pjsip_parser_err_report *err_list) - { -- pj_bool_t parsing_headers; -- pjsip_msg *msg = NULL; -+ /* These variables require "volatile" so their values get -+ * preserved when re-entering the PJ_TRY block after an error. -+ */ -+ volatile pj_bool_t parsing_headers; -+ pjsip_msg *volatile msg = NULL; -+ pjsip_ctype_hdr *volatile ctype_hdr = NULL; -+ - pj_str_t hname; -- pjsip_ctype_hdr *ctype_hdr = NULL; - pj_scanner *scanner = ctx->scanner; - pj_pool_t *pool = ctx->pool; - PJ_USE_EXCEPTION; -@@ -1023,7 +1099,6 @@ parse_headers: - hdr->name = hdr->sname = hname; - } - -- - /* Single parse of header line can produce multiple headers. - * For example, if one Contact: header contains Contact list - * separated by comma, then these Contacts will be split into -@@ -1267,7 +1342,7 @@ static void int_parse_uri_host_port( pj_scanner *scanner, - pj_str_t port; - pj_scan_get_char(scanner); - pj_scan_get(scanner, &pconst.pjsip_DIGIT_SPEC, &port); -- *p_port = pj_strtoul(&port); -+ strtoi_validate(&port, PJSIP_MIN_PORT, PJSIP_MAX_PORT, p_port); - } else { - *p_port = 0; - } -@@ -1458,8 +1533,8 @@ static void* int_parse_sip_url( pj_scanner *scanner, - url->transport_param = pvalue; - - } else if (!parser_stricmp(pname, pconst.pjsip_TTL_STR) && pvalue.slen) { -- url->ttl_param = pj_strtoul(&pvalue); -- -+ strtoi_validate(&pvalue, PJSIP_MIN_TTL, PJSIP_MAX_TTL, -+ &url->ttl_param); - } else if (!parser_stricmp(pname, pconst.pjsip_MADDR_STR) && pvalue.slen) { - url->maddr_param = pvalue; - -@@ -1595,7 +1670,8 @@ static void int_parse_status_line( pj_scanner *scanner, - - parse_sip_version(scanner); - pj_scan_get( scanner, &pconst.pjsip_DIGIT_SPEC, &token); -- status_line->code = pj_strtoul(&token); -+ strtoi_validate(&token, PJSIP_MIN_STATUS_CODE, PJSIP_MAX_STATUS_CODE, -+ &status_line->code); - if (*scanner->curptr != '\r' && *scanner->curptr != '\n') - pj_scan_get( scanner, &pconst.pjsip_NOT_NEWLINE, &status_line->reason); - else -@@ -1780,20 +1856,34 @@ static void int_parse_contact_param( pjsip_contact_hdr *hdr, - if (!parser_stricmp(pname, pconst.pjsip_Q_STR) && pvalue.slen) { - char *dot_pos = (char*) pj_memchr(pvalue.ptr, '.', pvalue.slen); - if (!dot_pos) { -- hdr->q1000 = pj_strtoul(&pvalue) * 1000; -+ strtoi_validate(&pvalue, PJSIP_MIN_Q1000, PJSIP_MAX_Q1000, -+ &hdr->q1000); -+ hdr->q1000 *= 1000; - } else { - pj_str_t tmp = pvalue; -+ unsigned long qval_frac; - - tmp.slen = dot_pos - pvalue.ptr; -- hdr->q1000 = pj_strtoul(&tmp) * 1000; -+ strtoi_validate(&tmp, PJSIP_MIN_Q1000, PJSIP_MAX_Q1000, -+ &hdr->q1000); -+ hdr->q1000 *= 1000; - - pvalue.slen = (pvalue.ptr+pvalue.slen) - (dot_pos+1); - pvalue.ptr = dot_pos + 1; -- hdr->q1000 += pj_strtoul_mindigit(&pvalue, 3); -+ if (pvalue.slen > 3) { -+ pvalue.slen = 3; -+ } -+ qval_frac = pj_strtoul_mindigit(&pvalue, 3); -+ if ((unsigned)hdr->q1000 > (PJ_MAXINT32 - qval_frac)) { -+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); -+ } -+ hdr->q1000 += qval_frac; - } -- } else if (!parser_stricmp(pname, pconst.pjsip_EXPIRES_STR) && pvalue.slen) { -- hdr->expires = pj_strtoul(&pvalue); -- -+ } else if (!parser_stricmp(pname, pconst.pjsip_EXPIRES_STR) && -+ pvalue.slen) -+ { -+ strtoi_validate(&pvalue, PJSIP_MIN_EXPIRES, PJSIP_MAX_EXPIRES, -+ &hdr->expires); - } else { - pjsip_param *p = PJ_POOL_ALLOC_T(pool, pjsip_param); - p->name = pname; -@@ -1890,19 +1980,22 @@ static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx ) - static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx ) - { - pj_str_t cseq, method; -- pjsip_cseq_hdr *hdr; -+ pjsip_cseq_hdr *hdr = NULL; -+ int cseq_val = 0; - -- hdr = pjsip_cseq_hdr_create(ctx->pool); - pj_scan_get( ctx->scanner, &pconst.pjsip_DIGIT_SPEC, &cseq); -- hdr->cseq = pj_strtoul(&cseq); -+ strtoi_validate(&cseq, PJSIP_MIN_CSEQ, PJSIP_MAX_CSEQ, &cseq_val); - -- pj_scan_get( ctx->scanner, &pconst.pjsip_TOKEN_SPEC, &method); -- pjsip_method_init_np(&hdr->method, &method); -+ hdr = pjsip_cseq_hdr_create(ctx->pool); -+ hdr->cseq = cseq_val; - -+ pj_scan_get( ctx->scanner, &pconst.pjsip_TOKEN_SPEC, &method); - parse_hdr_end( ctx->scanner ); - -- if (ctx->rdata) -+ pjsip_method_init_np(&hdr->method, &method); -+ if (ctx->rdata) { - ctx->rdata->msg_info.cseq = hdr; -+ } - - return (pjsip_hdr*)hdr; - } -@@ -1984,7 +2077,8 @@ static pjsip_hdr* parse_hdr_retry_after(pjsip_parse_ctx *ctx) - hdr = pjsip_retry_after_hdr_create(ctx->pool, 0); - - pj_scan_get(scanner, &pconst.pjsip_DIGIT_SPEC, &tmp); -- hdr->ivalue = pj_strtoul(&tmp); -+ strtoi_validate(&tmp, PJSIP_MIN_RETRY_AFTER, PJSIP_MAX_RETRY_AFTER, -+ &hdr->ivalue); - - while (!pj_scan_is_eof(scanner) && *scanner->curptr!='\r' && - *scanner->curptr!='\n') -@@ -2073,7 +2167,8 @@ static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner, - hdr->branch_param = pvalue; - - } else if (!parser_stricmp(pname, pconst.pjsip_TTL_STR) && pvalue.slen) { -- hdr->ttl_param = pj_strtoul(&pvalue); -+ strtoi_validate(&pvalue, PJSIP_MIN_TTL, PJSIP_MAX_TTL, -+ &hdr->ttl_param); - - } else if (!parser_stricmp(pname, pconst.pjsip_MADDR_STR) && pvalue.slen) { - hdr->maddr_param = pvalue; -@@ -2082,9 +2177,10 @@ static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner, - hdr->recvd_param = pvalue; - - } else if (!parser_stricmp(pname, pconst.pjsip_RPORT_STR)) { -- if (pvalue.slen) -- hdr->rport_param = pj_strtoul(&pvalue); -- else -+ if (pvalue.slen) { -+ strtoi_validate(&pvalue, PJSIP_MIN_PORT, PJSIP_MAX_PORT, -+ &hdr->rport_param); -+ } else - hdr->rport_param = 0; - } else { - pjsip_param *p = PJ_POOL_ALLOC_T(pool, pjsip_param); -@@ -2213,7 +2309,8 @@ static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx ) - pj_str_t digit; - pj_scan_get_char(scanner); - pj_scan_get(scanner, &pconst.pjsip_DIGIT_SPEC, &digit); -- hdr->sent_by.port = pj_strtoul(&digit); -+ strtoi_validate(&digit, PJSIP_MIN_PORT, PJSIP_MAX_PORT, -+ &hdr->sent_by.port); - } - - int_parse_via_param(hdr, scanner, ctx->pool); -@@ -2298,9 +2395,10 @@ PJ_DEF(pj_status_t) pjsip_parse_headers( pj_pool_t *pool, char *input, - unsigned options) - { - enum { STOP_ON_ERROR = 1 }; -+ pj_str_t hname; - pj_scanner scanner; - pjsip_parse_ctx ctx; -- pj_str_t hname; -+ - PJ_USE_EXCEPTION; - - pj_scan_init(&scanner, input, size, PJ_SCAN_AUTOSKIP_WS_HEADER, -@@ -2323,7 +2421,7 @@ retry_parse: - */ - hname.slen = 0; - -- /* Get hname. */ -+ /* Get hname. */ - pj_scan_get( &scanner, &pconst.pjsip_TOKEN_SPEC, &hname); - if (pj_scan_get_char( &scanner ) != ':') { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); -diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c -index f3be93beb..7ac3d1b76 100644 ---- a/pjsip/src/pjsip/sip_transaction.c -+++ b/pjsip/src/pjsip/sip_transaction.c -@@ -289,11 +289,11 @@ static pj_status_t create_tsx_key_2543( pj_pool_t *pool, - - /* Calculate length required. */ - len_required = method->name.slen + /* Method */ -- 9 + /* CSeq number */ -+ 11 + /* CSeq number */ - rdata->msg_info.from->tag.slen + /* From tag. */ - rdata->msg_info.cid->id.slen + /* Call-ID */ - host->slen + /* Via host. */ -- 9 + /* Via port. */ -+ 11 + /* Via port. */ - 16; /* Separator+Allowance. */ - key = p = (char*) pj_pool_alloc(pool, len_required); - -diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c -index 0c338e8fd..b24cbb411 100644 ---- a/pjsip/src/pjsip/sip_transport.c -+++ b/pjsip/src/pjsip/sip_transport.c -@@ -1848,7 +1848,7 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, - /* Check for parsing syntax error */ - if (msg==NULL || !pj_list_empty(&rdata->msg_info.parse_err)) { - pjsip_parser_err_report *err; -- char buf[128]; -+ char buf[256]; - pj_str_t tmp; - - /* Gather syntax error information */ -@@ -1862,7 +1862,10 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, - pj_exception_id_name(err->except_code), - (int)err->hname.slen, err->hname.ptr, - err->line, err->col); -- if (len > 0 && len < (int) (sizeof(buf)-tmp.slen)) { -+ if (len >= (int)sizeof(buf)-tmp.slen) { -+ len = (int)sizeof(buf)-tmp.slen; -+ } -+ if (len > 0) { - tmp.slen += len; - } - err = err->next; --- -2.13.6 - diff --git a/third-party/pjproject/patches/0021-sip_parser-Fix-return-code-in-pjsip_find_msg-and-add.patch b/third-party/pjproject/patches/0021-sip_parser-Fix-return-code-in-pjsip_find_msg-and-add.patch new file mode 100644 index 000000000..86d5aa7dd --- /dev/null +++ b/third-party/pjproject/patches/0021-sip_parser-Fix-return-code-in-pjsip_find_msg-and-add.patch @@ -0,0 +1,41 @@ +From 186f82627c40d0c3a56a6a94ce55c055ad1f7620 Mon Sep 17 00:00:00 2001 +From: George Joseph <gjoseph@digium.com> +Date: Fri, 10 Nov 2017 09:26:29 -0700 +Subject: [PATCH] sip_parser: Fix return code in pjsip_find_msg and add + "volatile" + +The default return code for pjsip_find_msg was PJ_SUCCESS so if +a Content-Length header wasn't found at all, pjsip_find_msg was +returning PJ_SUCCESS instead of PJSIP_EMISSINGHDR. + +Also added the volatile keyword to a few variables what are used +both insude and outsude the PJ_TRY/PJ_CATCH block. +--- + pjsip/src/pjsip/sip_parser.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c +index f9a0e65b5..f65ffca5a 100644 +--- a/pjsip/src/pjsip/sip_parser.c ++++ b/pjsip/src/pjsip/sip_parser.c +@@ -834,13 +834,13 @@ PJ_DEF(pj_status_t) pjsip_find_msg( const char *buf, pj_size_t size, + pj_bool_t is_datagram, pj_size_t *msg_size) + { + #if PJ_HAS_TCP +- const char *hdr_end; +- const char *body_start; ++ const char *volatile hdr_end; ++ const char *volatile body_start; + const char *pos; +- const char *line; ++ const char *volatile line; + int content_length = -1; + pj_str_t cur_msg; +- pj_status_t status = PJ_SUCCESS; ++ volatile pj_status_t status = PJSIP_EMISSINGHDR; + const pj_str_t end_hdr = { "\n\r\n", 3}; + + *msg_size = size; +-- +2.13.6 + diff --git a/third-party/pjproject/patches/0030-sip_transport-Destroy-transports-not-in-hash.patch b/third-party/pjproject/patches/0030-sip_transport-Destroy-transports-not-in-hash.patch new file mode 100644 index 000000000..e42b0f7c6 --- /dev/null +++ b/third-party/pjproject/patches/0030-sip_transport-Destroy-transports-not-in-hash.patch @@ -0,0 +1,27 @@ +diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c +index e4bec24..a39b56e 100644 +--- a/pjsip/src/pjsip/sip_transport.c ++++ b/pjsip/src/pjsip/sip_transport.c +@@ -957,7 +957,21 @@ static pj_bool_t is_transport_valid(pjsip_tpmgr *tpmgr, pjsip_transport *tp, + const pjsip_transport_key *key, + int key_len) + { +- return (pj_hash_get(tpmgr->table, key, key_len, NULL) == (void*)tp); ++ transport *tp_iter; ++ ++ if (pj_hash_get(tpmgr->table, key, key_len, NULL) == (void*)tp) { ++ return PJ_TRUE; ++ } ++ ++ tp_iter = tpmgr->tp_list.next; ++ while (tp_iter != &tpmgr->tp_list) { ++ if (tp_iter->tp == tp) { ++ return PJ_TRUE; ++ } ++ tp_iter = tp_iter->next; ++ } ++ ++ return PJ_FALSE; + } + + /* diff --git a/third-party/versions.mak b/third-party/versions.mak index 4f645f1bb..a90a52348 100644 --- a/third-party/versions.mak +++ b/third-party/versions.mak @@ -1,2 +1,2 @@ -PJPROJECT_VERSION = 2.7 +PJPROJECT_VERSION = 2.7.1 |