summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/res_config_mysql.c7
-rw-r--r--apps/app_directory.c18
-rw-r--r--apps/app_queue.c65
-rw-r--r--configs/samples/pjsip.conf.sample20
-rw-r--r--include/asterisk/config.h12
-rw-r--r--pbx/pbx_realtime.c13
-rw-r--r--res/res_config_curl.c7
-rw-r--r--res/res_config_ldap.c257
-rw-r--r--res/res_config_odbc.c6
-rw-r--r--res/res_config_pgsql.c9
-rw-r--r--res/res_config_sqlite.c8
-rw-r--r--res/res_config_sqlite3.c7
-rw-r--r--res/res_pjsip.c51
-rw-r--r--res/res_pjsip/pjsip_distributor.c109
-rw-r--r--res/res_pjsip_authenticator_digest.c105
-rw-r--r--res/res_pjsip_outbound_publish.c13
-rw-r--r--res/res_pjsip_outbound_registration.c13
-rw-r--r--third-party/pjproject/patches/0011-r5554-svn-backport-Increase-SENDER_WIDTH-column-size.patch77
-rw-r--r--third-party/pjproject/patches/0012-Re-1945-misc-Don-t-trigger-SRV-complete-callback-whe.patch59
-rw-r--r--third-party/pjproject/patches/0013-r5559-svn-backport-Fix-to-resolve-DNS-SRV-crashes.patch112
20 files changed, 743 insertions, 225 deletions
diff --git a/addons/res_config_mysql.c b/addons/res_config_mysql.c
index f2ef949fc..b080d118b 100644
--- a/addons/res_config_mysql.c
+++ b/addons/res_config_mysql.c
@@ -523,9 +523,8 @@ static struct ast_config *realtime_multi_mysql(const char *database, const char
while ((row = mysql_fetch_row(result))) {
var = NULL;
- cat = ast_category_new("", "", -1);
+ cat = ast_category_new_anonymous();
if (!cat) {
- ast_log(LOG_WARNING, "Out of memory!\n");
continue;
}
for (i = 0; i < numFields; i++) {
@@ -934,8 +933,8 @@ static struct ast_config *config_mysql(const char *database, const char *table,
}
if (strcmp(last, row[0]) || last_cat_metric != atoi(row[3])) {
- if (!(cur_cat = ast_category_new(row[0], "", -1))) {
- ast_log(LOG_WARNING, "Out of memory!\n");
+ cur_cat = ast_category_new_dynamic(row[0]);
+ if (!cur_cat) {
break;
}
strcpy(last, row[0]);
diff --git a/apps/app_directory.c b/apps/app_directory.c
index 642b9b23a..dd80c3467 100644
--- a/apps/app_directory.c
+++ b/apps/app_directory.c
@@ -464,7 +464,7 @@ static struct ast_config *realtime_directory(char *context)
struct ast_config *rtdata = NULL;
struct ast_category *cat;
struct ast_variable *var;
- char *mailbox;
+ char *category = NULL;
const char *fullname;
const char *hidefromdir, *searchcontexts = NULL;
struct ast_flags config_flags = { 0 };
@@ -505,13 +505,12 @@ static struct ast_config *realtime_directory(char *context)
return cfg;
}
- mailbox = NULL;
- while ( (mailbox = ast_category_browse(rtdata, mailbox)) ) {
- struct ast_variable *alias;
- const char *ctx = ast_variable_retrieve(rtdata, mailbox, "context");
+ while ((category = ast_category_browse(rtdata, category))) {
+ const char *mailbox = ast_variable_retrieve(rtdata, category, "mailbox");
+ const char *ctx = ast_variable_retrieve(rtdata, category, "context");
- fullname = ast_variable_retrieve(rtdata, mailbox, "fullname");
- hidefromdir = ast_variable_retrieve(rtdata, mailbox, "hidefromdir");
+ fullname = ast_variable_retrieve(rtdata, category, "fullname");
+ hidefromdir = ast_variable_retrieve(rtdata, category, "hidefromdir");
if (ast_true(hidefromdir)) {
/* Skip hidden */
continue;
@@ -519,8 +518,9 @@ static struct ast_config *realtime_directory(char *context)
/* password,Full Name,email,pager,options */
ast_str_set(&tmp, 0, "no-password,%s,,,", S_OR(fullname, ""));
- if (ast_variable_retrieve(rtdata, mailbox, "alias")) {
- for (alias = ast_variable_browse(rtdata, mailbox); alias; alias = alias->next) {
+ if (ast_variable_retrieve(rtdata, category, "alias")) {
+ struct ast_variable *alias;
+ for (alias = ast_variable_browse(rtdata, category); alias; alias = alias->next) {
if (!strcasecmp(alias->name, "alias")) {
ast_str_append(&tmp, 0, "|alias=%s", alias->value);
}
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 95f4c0eed..204b4b2de 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -2914,13 +2914,19 @@ static int load_realtime_rules(void)
ast_log(LOG_WARNING, "Failed to load queue rules from realtime\n");
return 0;
}
- while ((rulecat = ast_category_browse(cfg, rulecat)) && !ast_strlen_zero(rulecat)) {
- const char *timestr, *maxstr, *minstr;
+ while ((rulecat = ast_category_browse(cfg, rulecat))) {
+ const char *timestr, *maxstr, *minstr, *rule_name;
int penaltychangetime, rule_exists = 0, inserted = 0;
int max_penalty = 0, min_penalty = 0, min_relative = 0, max_relative = 0;
struct penalty_rule *new_penalty_rule = NULL;
+
+ rule_name = ast_variable_retrieve(cfg, rulecat, "rule_name");
+ if (ast_strlen_zero(rule_name)) {
+ continue;
+ }
+
AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
- if (!(strcasecmp(rl_iter->name, rulecat))) {
+ if (!(strcasecmp(rl_iter->name, rule_name))) {
rule_exists = 1;
new_rl = rl_iter;
break;
@@ -2931,13 +2937,13 @@ static int load_realtime_rules(void)
ast_config_destroy(cfg);
return -1;
}
- ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
+ ast_copy_string(new_rl->name, rule_name, sizeof(new_rl->name));
AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
}
timestr = ast_variable_retrieve(cfg, rulecat, "time");
if (!(timestr) || sscanf(timestr, "%30d", &penaltychangetime) != 1) {
ast_log(LOG_NOTICE, "Failed to parse time (%s) for one of the %s rules, skipping it\n",
- (ast_strlen_zero(timestr) ? "invalid value" : timestr), rulecat);
+ (ast_strlen_zero(timestr) ? "invalid value" : timestr), rule_name);
continue;
}
if (!(new_penalty_rule = ast_calloc(1, sizeof(*new_penalty_rule)))) {
@@ -3272,7 +3278,7 @@ static void member_remove_from_queue(struct call_queue *queue, struct member *me
* Search for member in queue, if found update penalty/paused state,
* if no member exists create one flag it as a RT member and add to queue member list.
*/
-static void rt_handle_member_record(struct call_queue *q, char *interface, struct ast_config *member_config)
+static void rt_handle_member_record(struct call_queue *q, char *category, struct ast_config *member_config)
{
struct member *m;
struct ao2_iterator mem_iter;
@@ -3282,11 +3288,12 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, struc
int ringinuse = q->ringinuse;
const char *config_val;
- const char *rt_uniqueid = ast_variable_retrieve(member_config, interface, "uniqueid");
- const char *membername = S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface);
- const char *state_interface = S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface);
- const char *penalty_str = ast_variable_retrieve(member_config, interface, "penalty");
- const char *paused_str = ast_variable_retrieve(member_config, interface, "paused");
+ const char *interface = ast_variable_retrieve(member_config, category, "interface");
+ const char *rt_uniqueid = ast_variable_retrieve(member_config, category, "uniqueid");
+ const char *membername = S_OR(ast_variable_retrieve(member_config, category, "membername"), interface);
+ const char *state_interface = S_OR(ast_variable_retrieve(member_config, category, "state_interface"), interface);
+ const char *penalty_str = ast_variable_retrieve(member_config, category, "penalty");
+ const char *paused_str = ast_variable_retrieve(member_config, category, "paused");
if (ast_strlen_zero(rt_uniqueid)) {
ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
@@ -3309,7 +3316,7 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, struc
}
}
- if ((config_val = ast_variable_retrieve(member_config, interface, realtime_ringinuse_field))) {
+ if ((config_val = ast_variable_retrieve(member_config, category, realtime_ringinuse_field))) {
if (ast_true(config_val)) {
ringinuse = 1;
} else if (ast_false(config_val)) {
@@ -3425,7 +3432,7 @@ static struct call_queue *find_queue_by_name_rt(const char *queuename, struct as
};
struct member *m;
struct ao2_iterator mem_iter;
- char *interface = NULL;
+ char *category = NULL;
const char *tmp_name;
char *tmp;
char tmpbuf[64]; /* Must be longer than the longest queue param name. */
@@ -3526,8 +3533,8 @@ static struct call_queue *find_queue_by_name_rt(const char *queuename, struct as
}
ao2_iterator_destroy(&mem_iter);
- while ((interface = ast_category_browse(member_config, interface))) {
- rt_handle_member_record(q, interface, member_config);
+ while ((category = ast_category_browse(member_config, category))) {
+ rt_handle_member_record(q, category, member_config);
}
/* Delete all realtime members that have been deleted in DB. */
@@ -3636,7 +3643,7 @@ static void update_realtime_members(struct call_queue *q)
{
struct ast_config *member_config = NULL;
struct member *m;
- char *interface = NULL;
+ char *category = NULL;
struct ao2_iterator mem_iter;
if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
@@ -3669,8 +3676,8 @@ static void update_realtime_members(struct call_queue *q)
}
ao2_iterator_destroy(&mem_iter);
- while ((interface = ast_category_browse(member_config, interface))) {
- rt_handle_member_record(q, interface, member_config);
+ while ((category = ast_category_browse(member_config, category))) {
+ rt_handle_member_record(q, category, member_config);
}
/* Delete all realtime members that have been deleted in DB. */
@@ -7471,12 +7478,11 @@ static int set_member_value(const char *queuename, const char *interface, int pr
if (ast_strlen_zero(queuename)) { /* This means we need to iterate through all the queues. */
if (ast_check_realtime("queues")) {
- char *name;
queue_config = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
if (queue_config) {
- for (name = ast_category_browse(queue_config, NULL);
- !ast_strlen_zero(name);
- name = ast_category_browse(queue_config, name)) {
+ char *category = NULL;
+ while ((category = ast_category_browse(queue_config, category))) {
+ const char *name = ast_variable_retrieve(queue_config, category, "name");
if ((q = find_load_queue_rt_friendly(name))) {
foundqueue++;
foundinterface += set_member_value_help_members(q, interface, property, value);
@@ -9381,9 +9387,10 @@ static char *__queues_show(struct mansession *s, int fd, int argc, const char *
* which have not yet been added to the in-core container
*/
struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
- char *queuename;
if (cfg) {
- for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
+ char *category = NULL;
+ while ((category = ast_category_browse(cfg, category))) {
+ const char *queuename = ast_variable_retrieve(cfg, category, "name");
if ((q = find_load_queue_rt_friendly(queuename))) {
queue_t_unref(q, "Done with temporary pointer");
}
@@ -9452,7 +9459,8 @@ static char *__queues_show(struct mansession *s, int fd, int argc, const char *
ast_str_set(&out, 0, " %s", mem->membername);
if (strcasecmp(mem->membername, mem->interface)) {
ast_str_append(&out, 0, " (%s", mem->interface);
- if (!ast_strlen_zero(mem->state_interface)) {
+ if (!ast_strlen_zero(mem->state_interface)
+ && strcmp(mem->state_interface, mem->interface)) {
ast_str_append(&out, 0, " from %s", mem->state_interface);
}
ast_str_append(&out, 0, ")");
@@ -11044,14 +11052,13 @@ static int queues_data_provider_get(const struct ast_data_search *search,
struct ao2_iterator i;
struct call_queue *queue, *queue_realtime = NULL;
struct ast_config *cfg;
- char *queuename;
/* load realtime queues. */
cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
if (cfg) {
- for (queuename = ast_category_browse(cfg, NULL);
- !ast_strlen_zero(queuename);
- queuename = ast_category_browse(cfg, queuename)) {
+ char *category = NULL;
+ while ((category = ast_category_browse(cfg, category))) {
+ const char *queuename = ast_variable_retrieve(cfg, category, "name");
if ((queue = find_load_queue_rt_friendly(queuename))) {
queue_unref(queue);
}
diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index 6595423c9..323100b00 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -12,6 +12,12 @@
; If you want to see more detail please check the documentation sources
; mentioned at the top of this file.
+; ============================================================================
+; NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
+;
+; This file does not maintain the complete option documentation.
+; ============================================================================
+
; Documentation
;
; The official documentation is at http://wiki.asterisk.org
@@ -765,6 +771,14 @@
;==========================AUTH SECTION OPTIONS=========================
;[auth]
; SYNOPSIS: Authentication type
+;
+; Note: Using the same auth section for inbound and outbound
+; authentication is not recommended. There is a difference in
+; meaning for an empty realm setting between inbound and outbound
+; authentication uses. Look to the CLI config help
+; "config show help res_pjsip auth realm" or on the wiki for the
+; difference.
+;
;auth_type=userpass ; Authentication type (default: "userpass")
;nonce_lifetime=32 ; Lifetime of a nonce associated with this
; authentication config (default: "32")
@@ -959,9 +973,9 @@
; From header username will be set to this value if
; there is no better option (such as CallerID or
; endpoint/from_user) to be used
-;default_realm=asterisk ; When Asterisk generates a challenge, the realm will be
- ; set to this value if there is no better option (such as
- ; auth/realm) to be used
+;default_realm=asterisk ; When Asterisk generates a challenge, the digest realm
+ ; will be set to this value if there is no better option
+ ; (such as auth/realm) to be used.
; Asterisk Task Processor Queue Size
; On heavy loaded system with DB storage you may need to increase
diff --git a/include/asterisk/config.h b/include/asterisk/config.h
index 4944a3af2..f57966b0b 100644
--- a/include/asterisk/config.h
+++ b/include/asterisk/config.h
@@ -834,6 +834,18 @@ const char *ast_config_option(struct ast_config *cfg, const char *cat, const cha
struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno);
/*!
+ * \brief Create a category that is not backed by a file
+ *
+ * \param name name of new category
+ */
+#define ast_category_new_dynamic(name) ast_category_new(name, "", -1)
+
+/*!
+ * \brief Create a nameless category that is not backed by a file
+ */
+#define ast_category_new_anonymous() ast_category_new_dynamic("")
+
+/*!
* \brief Create a category making it a template
*
* \param name name of new template
diff --git a/pbx/pbx_realtime.c b/pbx/pbx_realtime.c
index 08c90aa62..6f5d13738 100644
--- a/pbx/pbx_realtime.c
+++ b/pbx/pbx_realtime.c
@@ -189,25 +189,26 @@ static struct ast_variable *realtime_switch_common(const char *table, const char
if (!var && !ast_test_flag(&flags, OPTION_PATTERNS_DISABLED)) {
cfg = ast_load_realtime_multientry(table, "exten LIKE", "\\_%", "context", context, "priority", pri, SENTINEL);
if (cfg) {
- char *cat = ast_category_browse(cfg, NULL);
+ char *cat = NULL;
+
+ while ((cat = ast_category_browse(cfg, cat))) {
+ const char *realtime_exten = ast_variable_retrieve(cfg, cat, "exten");
- while(cat) {
switch(mode) {
case MODE_MATCHMORE:
- match = ast_extension_close(cat, exten, 1);
+ match = ast_extension_close(realtime_exten, exten, 1);
break;
case MODE_CANMATCH:
- match = ast_extension_close(cat, exten, 0);
+ match = ast_extension_close(realtime_exten, exten, 0);
break;
case MODE_MATCH:
default:
- match = ast_extension_match(cat, exten);
+ match = ast_extension_match(realtime_exten, exten);
}
if (match) {
var = ast_category_detach_variables(ast_category_get(cfg, cat, NULL));
break;
}
- cat = ast_category_browse(cfg, cat);
}
ast_config_destroy(cfg);
}
diff --git a/res/res_config_curl.c b/res/res_config_curl.c
index 9ad7a6e3d..06a6aef89 100644
--- a/res/res_config_curl.c
+++ b/res/res_config_curl.c
@@ -182,7 +182,8 @@ static struct ast_config *realtime_multi_curl(const char *url, const char *unuse
continue;
}
- if (!(cat = ast_category_new("", "", 99999))) {
+ cat = ast_category_new_anonymous();
+ if (!cat) {
continue;
}
@@ -569,8 +570,10 @@ static struct ast_config *config_curl(const char *url, const char *unused, const
}
if (!cat || strcmp(category, cur_cat) || last_cat_metric != cat_metric) {
- if (!(cat = ast_category_new(category, "", 99999)))
+ cat = ast_category_new_dynamic(category);
+ if (!cat) {
break;
+ }
cur_cat = category;
last_cat_metric = cat_metric;
ast_category_append(cfg, cat);
diff --git a/res/res_config_ldap.c b/res/res_config_ldap.c
index 5e95853d4..8ad9e1319 100644
--- a/res/res_config_ldap.c
+++ b/res/res_config_ldap.c
@@ -190,7 +190,7 @@ static int semicolon_count_var(struct ast_variable *var)
return 0;
}
- ast_debug(2, "LINE(%d) semicolon_count_var: %s\n", __LINE__, var_value->value);
+ ast_debug(2, "semicolon_count_var: %s\n", var_value->value);
return semicolon_count_str(var_value->value);
}
@@ -330,7 +330,7 @@ static struct ast_variable *realtime_ldap_entry_to_var(struct ldap_table_config
for (v = values; *v; v++) {
value = *v;
valptr = value->bv_val;
- ast_debug(2, "LINE(%d) attribute_name: %s LDAP value: %s\n", __LINE__, attribute_name, valptr);
+ ast_debug(2, "attribute_name: %s LDAP value: %s\n", attribute_name, valptr);
if (is_realmed_password_attribute) {
if (!strncasecmp(valptr, "{md5}", 5)) {
valptr += 5;
@@ -422,7 +422,7 @@ static struct ast_variable **realtime_ldap_result_to_vars(struct ldap_table_conf
* value in \a variable_value; otherwise, we keep \a vars static and increase the length of the linked list of variables in the array element.
* This memory must be freed outside of this function.
*/
- vars = ast_calloc(sizeof(struct ast_variable *), tot_count + 1);
+ vars = ast_calloc(tot_count + 1, sizeof(struct ast_variable *));
ldap_entry = ldap_first_entry(ldapConn, ldap_result_msg);
@@ -467,7 +467,7 @@ static struct ast_variable **realtime_ldap_result_to_vars(struct ldap_table_conf
delim_value = ast_strdup(valptr);
if ((delim_tot_count = semicolon_count_str(delim_value)) > 0) {
- ast_debug(4, "LINE(%d) is delimited %d times: %s\n", __LINE__, delim_tot_count, delim_value);
+ ast_debug(4, "is delimited %d times: %s\n", delim_tot_count, delim_value);
is_delimited = 1;
}
}
@@ -477,11 +477,11 @@ static struct ast_variable **realtime_ldap_result_to_vars(struct ldap_table_conf
/* for non-Static RealTime, first */
for (i = pos; !ast_strlen_zero(valptr + i); i++) {
- ast_debug(4, "LINE(%d) DELIM pos: %d i: %d\n", __LINE__, pos, i);
+ ast_debug(4, "DELIM pos: %d i: %d\n", pos, i);
if (delim_value[i] == ';') {
delim_value[i] = '\0';
- ast_debug(2, "LINE(%d) DELIM - attribute_name: %s value: %s pos: %d\n", __LINE__, attribute_name, &delim_value[pos], pos);
+ ast_debug(2, "DELIM - attribute_name: %s value: %s pos: %d\n", attribute_name, &delim_value[pos], pos);
if (prev) {
prev->next = ast_variable_new(attribute_name, &delim_value[pos], table_config->table_name);
@@ -499,9 +499,9 @@ static struct ast_variable **realtime_ldap_result_to_vars(struct ldap_table_conf
}
}
if (ast_strlen_zero(valptr + i)) {
- ast_debug(4, "LINE(%d) DELIM pos: %d i: %d delim_count: %d\n", __LINE__, pos, i, delim_count);
+ ast_debug(4, "DELIM pos: %d i: %d delim_count: %d\n", pos, i, delim_count);
/* Last delimited value */
- ast_debug(4, "LINE(%d) DELIM - attribute_name: %s value: %s pos: %d\n", __LINE__, attribute_name, &delim_value[pos], pos);
+ ast_debug(4, "DELIM - attribute_name: %s value: %s pos: %d\n", attribute_name, &delim_value[pos], pos);
if (prev) {
prev->next = ast_variable_new(attribute_name, &delim_value[pos], table_config->table_name);
if (prev->next) {
@@ -517,14 +517,14 @@ static struct ast_variable **realtime_ldap_result_to_vars(struct ldap_table_conf
ast_free(delim_value);
delim_value = NULL;
- ast_debug(4, "LINE(%d) DELIM pos: %d i: %d\n", __LINE__, pos, i);
+ ast_debug(4, "DELIM pos: %d i: %d\n", pos, i);
} else {
/* not delimited */
if (delim_value) {
ast_free(delim_value);
delim_value = NULL;
}
- ast_debug(2, "LINE(%d) attribute_name: %s value: %s\n", __LINE__, attribute_name, valptr);
+ ast_debug(2, "attribute_name: %s value: %s\n", attribute_name, valptr);
if (prev) {
prev->next = ast_variable_new(attribute_name, valptr, table_config->table_name);
@@ -548,7 +548,7 @@ static struct ast_variable **realtime_ldap_result_to_vars(struct ldap_table_conf
const struct ast_variable *tmpdebug = variable_named(var, "variable_name");
const struct ast_variable *tmpdebug2 = variable_named(var, "variable_value");
if (tmpdebug && tmpdebug2) {
- ast_debug(3, "LINE(%d) Added to vars - %s = %s\n", __LINE__, tmpdebug->value, tmpdebug2->value);
+ ast_debug(3, "Added to vars - %s = %s\n", tmpdebug->value, tmpdebug2->value);
}
}
vars[entry_index++] = var;
@@ -559,7 +559,7 @@ static struct ast_variable **realtime_ldap_result_to_vars(struct ldap_table_conf
} while (delim_count <= delim_tot_count && static_table_config == table_config);
if (static_table_config != table_config) {
- ast_debug(3, "LINE(%d) Added to vars - non static\n", __LINE__);
+ ast_debug(3, "Added to vars - non static\n");
vars[entry_index++] = var;
prev = NULL;
@@ -926,13 +926,8 @@ static struct ast_variable **realtime_ldap_base_ap(unsigned int *entries_count_p
}
}
- if (filter) {
- ast_free(filter);
- }
-
- if (clean_basedn) {
- ast_free(clean_basedn);
- }
+ ast_free(filter);
+ ast_free(clean_basedn);
ast_mutex_unlock(&ldap_lock);
@@ -1043,10 +1038,8 @@ static struct ast_config *realtime_multi_ldap(const char *basedn,
struct ast_variable **p = vars;
while (*p) {
- struct ast_category *cat = NULL;
- cat = ast_category_new("", table_name, -1);
+ struct ast_category *cat = ast_category_new_anonymous();
if (!cat) {
- ast_log(LOG_ERROR, "Unable to create a new category!\n");
break;
} else {
struct ast_variable *var = *p;
@@ -1136,7 +1129,7 @@ static struct ast_config *config_ldap(const char *basedn, const char *table_name
* first, and since the data could easily exceed stack size, this is
* allocated from the heap.
*/
- if (!(categories = ast_calloc(sizeof(*categories), vars_count))) {
+ if (!(categories = ast_calloc(vars_count, sizeof(*categories)))) {
return NULL;
}
@@ -1195,7 +1188,7 @@ static struct ast_config *config_ldap(const char *basedn, const char *table_name
if (!last_category || strcmp(last_category, categories[i].name) ||
last_category_metric != categories[i].metric) {
- cur_cat = ast_category_new(categories[i].name, table_name, -1);
+ cur_cat = ast_category_new_dynamic(categories[i].name);
if (!cur_cat) {
break;
}
@@ -1217,6 +1210,90 @@ static struct ast_config *config_ldap(const char *basedn, const char *table_name
return cfg;
}
+/*!
+ * \internal
+ * \brief Remove LDAP_MOD_DELETE modifications that will not succeed
+ *
+ * \details
+ * A LDAP_MOD_DELETE operation will fail if the LDAP entry does not already have
+ * the corresponding attribute. Because we may be updating multiple LDAP entries
+ * in a single call to update_ldap(), we may need our own copy of the
+ * modifications array for each one.
+ *
+ * \note
+ * This function dynamically allocates memory. If it returns a non-NULL pointer,
+ * it is up to the caller to free it with ldap_mods_free()
+ *
+ * \returns an LDAPMod * if modifications needed to be removed, NULL otherwise.
+ */
+static LDAPMod **massage_mods_for_entry(LDAPMessage *entry, LDAPMod **mods, size_t count)
+{
+ size_t i;
+ int remove[count];
+ size_t remove_count = 0;
+
+ for (i = 0; i < count; i++) {
+ BerElement *ber = NULL;
+ char *attribute;
+ int exists = 0;
+
+ if (mods[i]->mod_op != LDAP_MOD_DELETE) {
+ continue;
+ }
+
+ /* If we are deleting something, it has to exist */
+ attribute = ldap_first_attribute(ldapConn, entry, &ber);
+ while (attribute) {
+ if (!strcasecmp(attribute, mods[i]->mod_type)) {
+ /* OK, we have the attribute */
+ exists = 1;
+ ldap_memfree(attribute);
+ break;
+ }
+
+ ldap_memfree(attribute);
+ attribute = ldap_next_attribute(ldapConn, entry, ber);
+ }
+
+ if (!exists) {
+ remove[remove_count++] = i;
+ }
+ }
+
+ if (remove_count) {
+ size_t k, remove_index;
+ LDAPMod **x = ldap_memcalloc(count - remove_count + 1, sizeof(LDAPMod *));
+ for (i = 0, k = 0; i < count; i++) {
+ int skip = 0;
+ /* Is this one we have to remove? */
+ for (remove_index = 0; !skip && remove_index < remove_count; remove_index++) {
+ skip = (remove[remove_index] == i);
+ }
+
+ if (skip) {
+ ast_debug(3, "Skipping %s deletion because it doesn't exist\n",
+ mods[i]->mod_type);
+ continue;
+ }
+
+ x[k] = ldap_memcalloc(1, sizeof(LDAPMod));
+ x[k]->mod_op = mods[i]->mod_op;
+ x[k]->mod_type = ldap_strdup(mods[i]->mod_type);
+ if (mods[i]->mod_values) {
+ x[k]->mod_values = ldap_memcalloc(2, sizeof(char *));
+ x[k]->mod_values[0] = ldap_strdup(mods[i]->mod_values[0]);
+ }
+ k++;
+ }
+ /* NULL terminate */
+ x[k] = NULL;
+ return x;
+ }
+
+ return NULL;
+}
+
+
/* \brief Function to update a set of values in ldap static mode
*/
static int update_ldap(const char *basedn, const char *table_name, const char *attribute,
@@ -1249,7 +1326,7 @@ static int update_ldap(const char *basedn, const char *table_name, const char *a
}
if (!attribute || !lookup) {
- ast_log(LOG_WARNING, "LINE(%d): search parameters are empty.\n", __LINE__);
+ ast_log(LOG_WARNING, "Search parameters are empty.\n");
return -1;
}
ast_mutex_lock(&ldap_lock);
@@ -1285,19 +1362,22 @@ static int update_ldap(const char *basedn, const char *table_name, const char *a
* one parameter/value pair and delimit them with a semicolon */
newparam = convert_attribute_name_to_ldap(table_config, field->name);
if (!newparam) {
- ast_log(LOG_WARNING, "LINE(%d): need at least one parameter to modify.\n", __LINE__);
+ ast_log(LOG_WARNING, "Need at least one parameter to modify.\n");
return -1;
}
mods_size = 2; /* one for the first param/value pair and one for the the terminating NULL */
- ldap_mods = ldap_memcalloc(sizeof(LDAPMod *), mods_size);
+ ldap_mods = ldap_memcalloc(mods_size, sizeof(LDAPMod *));
ldap_mods[0] = ldap_memcalloc(1, sizeof(LDAPMod));
-
- ldap_mods[0]->mod_op = LDAP_MOD_REPLACE;
ldap_mods[0]->mod_type = ldap_strdup(newparam);
- ldap_mods[0]->mod_values = ast_calloc(sizeof(char *), 2);
- ldap_mods[0]->mod_values[0] = ldap_strdup(field->value);
+ if (strlen(field->value) == 0) {
+ ldap_mods[0]->mod_op = LDAP_MOD_DELETE;
+ } else {
+ ldap_mods[0]->mod_op = LDAP_MOD_REPLACE;
+ ldap_mods[0]->mod_values = ldap_memcalloc(2, sizeof(char *));
+ ldap_mods[0]->mod_values[0] = ldap_strdup(field->value);
+ }
while ((field = field->next)) {
newparam = convert_attribute_name_to_ldap(table_config, field->name);
@@ -1309,7 +1389,7 @@ static int update_ldap(const char *basedn, const char *table_name, const char *a
ldap_mods[i]->mod_values[0] = ldap_memrealloc(ldap_mods[i]->mod_values[0], sizeof(char) * (strlen(ldap_mods[i]->mod_values[0]) + strlen(field->value) + 2));
strcat(ldap_mods[i]->mod_values[0], ";");
strcat(ldap_mods[i]->mod_values[0], field->value);
- mod_exists = 1;
+ mod_exists = 1;
break;
}
}
@@ -1318,22 +1398,19 @@ static int update_ldap(const char *basedn, const char *table_name, const char *a
if (!mod_exists) {
mods_size++;
ldap_mods = ldap_memrealloc(ldap_mods, sizeof(LDAPMod *) * mods_size);
- ldap_mods[mods_size - 1] = NULL;
-
ldap_mods[mods_size - 2] = ldap_memcalloc(1, sizeof(LDAPMod));
-
- ldap_mods[mods_size - 2]->mod_type = ldap_memcalloc(sizeof(char), strlen(newparam) + 1);
- strcpy(ldap_mods[mods_size - 2]->mod_type, newparam);
+ ldap_mods[mods_size - 2]->mod_type = ldap_strdup(newparam);
if (strlen(field->value) == 0) {
ldap_mods[mods_size - 2]->mod_op = LDAP_MOD_DELETE;
} else {
ldap_mods[mods_size - 2]->mod_op = LDAP_MOD_REPLACE;
-
- ldap_mods[mods_size - 2]->mod_values = ldap_memcalloc(sizeof(char *), 2);
- ldap_mods[mods_size - 2]->mod_values[0] = ldap_memcalloc(sizeof(char), strlen(field->value) + 1);
- strcpy(ldap_mods[mods_size - 2]->mod_values[0], field->value);
+ ldap_mods[mods_size - 2]->mod_values = ldap_memcalloc(2, sizeof(char *));
+ ldap_mods[mods_size - 2]->mod_values[0] = ldap_strdup(field->value);
}
+
+ /* NULL terminate */
+ ldap_mods[mods_size - 1] = NULL;
}
}
/* freeing ldap_mods further down */
@@ -1366,27 +1443,45 @@ static int update_ldap(const char *basedn, const char *table_name, const char *a
ast_free(filter);
ast_free(clean_basedn);
ldap_msgfree(ldap_result_msg);
- ldap_mods_free(ldap_mods, 0);
+ ldap_mods_free(ldap_mods, 1);
return -1;
}
/* Ready to update */
if ((num_entries = ldap_count_entries(ldapConn, ldap_result_msg)) > 0) {
- ast_debug(3, "LINE(%d) Modifying %s=%s hits: %d\n", __LINE__, attribute, lookup, num_entries);
+ ast_debug(3, "Modifying %s=%s hits: %d\n", attribute, lookup, num_entries);
for (i = 0; option_debug > 2 && i < mods_size - 1; i++) {
if (ldap_mods[i]->mod_op != LDAP_MOD_DELETE) {
- ast_debug(3, "LINE(%d) %s=%s \n", __LINE__, ldap_mods[i]->mod_type, ldap_mods[i]->mod_values[0]);
+ ast_debug(3, "%s=%s\n", ldap_mods[i]->mod_type, ldap_mods[i]->mod_values[0]);
} else {
- ast_debug(3, "LINE(%d) deleting %s \n", __LINE__, ldap_mods[i]->mod_type);
+ ast_debug(3, "deleting %s\n", ldap_mods[i]->mod_type);
}
}
ldap_entry = ldap_first_entry(ldapConn, ldap_result_msg);
- for (i = 0; ldap_entry; i++) {
+ for (i = 0; ldap_entry; i++) {
+ LDAPMod **working = ldap_mods;
+ LDAPMod **massaged = massage_mods_for_entry(ldap_entry, ldap_mods, mods_size - 1);
+
+ if (massaged) {
+ /* Did we massage everything out of the list? */
+ if (massaged[0] == NULL) {
+ ast_debug(3, "Nothing left to modify - skipping\n");
+ ldap_mods_free(massaged, 1);
+ continue;
+ }
+ working = massaged;
+ }
+
dn = ldap_get_dn(ldapConn, ldap_entry);
- if ((error = ldap_modify_ext_s(ldapConn, dn, ldap_mods, NULL, NULL)) != LDAP_SUCCESS) {
+ if ((error = ldap_modify_ext_s(ldapConn, dn, working, NULL, NULL)) != LDAP_SUCCESS) {
ast_log(LOG_ERROR, "Couldn't modify '%s'='%s', dn:%s because %s\n",
attribute, lookup, dn, ldap_err2string(error));
}
+
+ if (massaged) {
+ ldap_mods_free(massaged, 1);
+ }
+
ldap_memfree(dn);
ldap_entry = ldap_next_entry(ldapConn, ldap_entry);
}
@@ -1396,7 +1491,7 @@ static int update_ldap(const char *basedn, const char *table_name, const char *a
ast_free(filter);
ast_free(clean_basedn);
ldap_msgfree(ldap_result_msg);
- ldap_mods_free(ldap_mods, 0);
+ ldap_mods_free(ldap_mods, 1);
return num_entries;
}
@@ -1469,23 +1564,19 @@ static int update2_ldap(const char *basedn, const char *table_name, const struct
field = update_fields;
newparam = convert_attribute_name_to_ldap(table_config, field->name);
if (!newparam) {
- ast_log(LOG_WARNING, "LINE(%d): need at least one parameter to modify.\n", __LINE__);
+ ast_log(LOG_WARNING, "Need at least one parameter to modify.\n");
ast_free(filter);
ast_free(clean_basedn);
return -1;
}
mods_size = 2; /* one for the first param/value pair and one for the the terminating NULL */
- ldap_mods = ast_calloc(sizeof(LDAPMod *), mods_size);
- ldap_mods[0] = ast_calloc(1, sizeof(LDAPMod));
-
+ ldap_mods = ldap_memcalloc(mods_size, sizeof(LDAPMod *));
+ ldap_mods[0] = ldap_memcalloc(1, sizeof(LDAPMod));
ldap_mods[0]->mod_op = LDAP_MOD_REPLACE;
- ldap_mods[0]->mod_type = ast_calloc(sizeof(char), strlen(newparam) + 1);
- strcpy(ldap_mods[0]->mod_type, newparam);
-
- ldap_mods[0]->mod_values = ast_calloc(sizeof(char), 2);
- ldap_mods[0]->mod_values[0] = ast_calloc(sizeof(char), strlen(field->value) + 1);
- strcpy(ldap_mods[0]->mod_values[0], field->value);
+ ldap_mods[0]->mod_type = ldap_strdup(newparam);
+ ldap_mods[0]->mod_values = ldap_memcalloc(2, sizeof(char *));
+ ldap_mods[0]->mod_values[0] = ldap_strdup(field->value);
while ((field = field->next)) {
newparam = convert_attribute_name_to_ldap(table_config, field->name);
@@ -1505,18 +1596,15 @@ static int update2_ldap(const char *basedn, const char *table_name, const struct
/* create new mod */
if (!mod_exists) {
mods_size++;
- ldap_mods = ast_realloc(ldap_mods, sizeof(LDAPMod *) * mods_size);
- ldap_mods[mods_size - 1] = NULL;
- ldap_mods[mods_size - 2] = ast_calloc(1, sizeof(LDAPMod));
-
+ ldap_mods = ldap_memrealloc(ldap_mods, sizeof(LDAPMod *) * mods_size);
+ ldap_mods[mods_size - 2] = ldap_memcalloc(1, sizeof(LDAPMod));
ldap_mods[mods_size - 2]->mod_op = LDAP_MOD_REPLACE;
+ ldap_mods[mods_size - 2]->mod_type = ldap_strdup(newparam);
+ ldap_mods[mods_size - 2]->mod_values = ldap_memcalloc(2, sizeof(char *));
+ ldap_mods[mods_size - 2]->mod_values[0] = ldap_strdup(field->value);
- ldap_mods[mods_size - 2]->mod_type = ast_calloc(sizeof(char), strlen(newparam) + 1);
- strcpy(ldap_mods[mods_size - 2]->mod_type, newparam);
-
- ldap_mods[mods_size - 2]->mod_values = ast_calloc(sizeof(char *), 2);
- ldap_mods[mods_size - 2]->mod_values[0] = ast_calloc(sizeof(char), strlen(field->value) + 1);
- strcpy(ldap_mods[mods_size - 2]->mod_values[0], field->value);
+ /* NULL terminate */
+ ldap_mods[mods_size - 1] = NULL;
}
}
/* freeing ldap_mods further down */
@@ -1550,13 +1638,13 @@ static int update2_ldap(const char *basedn, const char *table_name, const struct
ast_free(filter);
ast_free(clean_basedn);
ldap_msgfree(ldap_result_msg);
- ldap_mods_free(ldap_mods, 0);
+ ldap_mods_free(ldap_mods, 1);
return -1;
}
/* Ready to update */
if ((num_entries = ldap_count_entries(ldapConn, ldap_result_msg)) > 0) {
for (i = 0; option_debug > 2 && i < mods_size - 1; i++) {
- ast_debug(3, "LINE(%d) %s=%s \n", __LINE__, ldap_mods[i]->mod_type, ldap_mods[i]->mod_values[0]);
+ ast_debug(3, "%s=%s\n", ldap_mods[i]->mod_type, ldap_mods[i]->mod_values[0]);
}
ldap_entry = ldap_first_entry(ldapConn, ldap_result_msg);
@@ -1572,14 +1660,10 @@ static int update2_ldap(const char *basedn, const char *table_name, const struct
}
ast_mutex_unlock(&ldap_lock);
- if (filter) {
- ast_free(filter);
- }
- if (clean_basedn) {
- ast_free(clean_basedn);
- }
+ ast_free(filter);
+ ast_free(clean_basedn);
ldap_msgfree(ldap_result_msg);
- ldap_mods_free(ldap_mods, 0);
+ ldap_mods_free(ldap_mods, 1);
return num_entries;
}
@@ -1681,6 +1765,21 @@ static int reload(void)
return 0;
}
+static int config_can_be_inherited(const char *key)
+{
+ int i;
+ static const char * const config[] = {
+ "basedn", "host", "pass", "port", "protocol", "url", "user", "version", NULL
+ };
+
+ for (i = 0; config[i]; i++) {
+ if (!strcasecmp(key, config[i])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
/*! \brief parse the configuration file
*/
static int parse_config(void)
@@ -1771,7 +1870,9 @@ static int parse_config(void)
if (!strcasecmp(var->name, "additionalFilter")) {
table_config->additional_filter = ast_strdup(var->value);
} else {
- ldap_table_config_add_attribute(table_config, var->name, var->value);
+ if (!is_general || config_can_be_inherited(var->name)) {
+ ldap_table_config_add_attribute(table_config, var->name, var->value);
+ }
}
}
}
diff --git a/res/res_config_odbc.c b/res/res_config_odbc.c
index 5a25b6b5a..114708325 100644
--- a/res/res_config_odbc.c
+++ b/res/res_config_odbc.c
@@ -407,9 +407,8 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
ast_log(LOG_WARNING, "SQL Fetch error! [%s]\n", ast_str_buffer(sql));
continue;
}
- cat = ast_category_new("","",99999);
+ cat = ast_category_new_anonymous();
if (!cat) {
- ast_log(LOG_WARNING, "Out of memory!\n");
continue;
}
for (x=0;x<colcount;x++) {
@@ -1016,9 +1015,8 @@ static struct ast_config *config_odbc(const char *database, const char *table, c
continue;
}
if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
- cur_cat = ast_category_new(q.category, "", 99999);
+ cur_cat = ast_category_new_dynamic(q.category);
if (!cur_cat) {
- ast_log(LOG_WARNING, "Out of memory!\n");
break;
}
strcpy(last, q.category);
diff --git a/res/res_config_pgsql.c b/res/res_config_pgsql.c
index f0859617d..b5f24b0f4 100644
--- a/res/res_config_pgsql.c
+++ b/res/res_config_pgsql.c
@@ -648,8 +648,10 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
var = NULL;
- if (!(cat = ast_category_new("","",99999)))
+ cat = ast_category_new_anonymous();
+ if (!cat) {
continue;
+ }
for (i = 0; i < numFields; i++) {
stringp = PQgetvalue(result, rowIndex, i);
while (stringp) {
@@ -1129,9 +1131,10 @@ static struct ast_config *config_pgsql(const char *database, const char *table,
}
if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
- cur_cat = ast_category_new(field_category, "", 99999);
- if (!cur_cat)
+ cur_cat = ast_category_new_dynamic(field_category);
+ if (!cur_cat) {
break;
+ }
ast_copy_string(last, field_category, sizeof(last));
last_cat_metric = atoi(field_cat_metric);
ast_category_append(cfg, cur_cat);
diff --git a/res/res_config_sqlite.c b/res/res_config_sqlite.c
index 685949787..96a10b43f 100644
--- a/res/res_config_sqlite.c
+++ b/res/res_config_sqlite.c
@@ -874,10 +874,8 @@ static int add_cfg_entry(void *arg, int argc, char **argv, char **columnNames)
}
if (!args->cat_name || strcmp(args->cat_name, argv[RES_CONFIG_SQLITE_CONFIG_CATEGORY])) {
- args->cat = ast_category_new(argv[RES_CONFIG_SQLITE_CONFIG_CATEGORY], "", 99999);
-
+ args->cat = ast_category_new_dynamic(argv[RES_CONFIG_SQLITE_CONFIG_CATEGORY]);
if (!args->cat) {
- ast_log(LOG_WARNING, "Unable to allocate category\n");
return 1;
}
@@ -1086,8 +1084,8 @@ static int add_rt_multi_cfg_entry(void *arg, int argc, char **argv, char **colum
return 1;
}
- if (!(cat = ast_category_new(cat_name, "", 99999))) {
- ast_log(LOG_WARNING, "Unable to allocate category\n");
+ cat = ast_category_new_dynamic(cat_name);
+ if (!cat) {
return 1;
}
diff --git a/res/res_config_sqlite3.c b/res/res_config_sqlite3.c
index 8c514b07c..087843a68 100644
--- a/res/res_config_sqlite3.c
+++ b/res/res_config_sqlite3.c
@@ -503,7 +503,8 @@ static int append_row_to_cfg(void *arg, int num_columns, char **values, char **c
struct ast_category *cat;
int i;
- if (!(cat = ast_category_new("", "", 99999))) {
+ cat = ast_category_new_anonymous();
+ if (!cat) {
return SQLITE_ABORT;
}
@@ -723,8 +724,8 @@ static int static_realtime_cb(void *arg, int num_columns, char **values, char **
}
if (!args->cat_name || strcmp(args->cat_name, values[COL_CATEGORY])) {
- if (!(args->cat = ast_category_new(values[COL_CATEGORY], "", 99999))) {
- ast_log(LOG_WARNING, "Unable to allocate category\n");
+ args->cat = ast_category_new_dynamic(values[COL_CATEGORY]);
+ if (!args->cat) {
return SQLITE_ABORT;
}
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 90eb37263..4dead21f5 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -112,9 +112,15 @@
This is a comma-delimited list of <replaceable>auth</replaceable> sections defined
in <filename>pjsip.conf</filename> to be used to verify inbound connection attempts.
</para><para>
- Endpoints without an <literal>authentication</literal> object
- configured will allow connections without vertification.
- </para></description>
+ Endpoints without an authentication object
+ configured will allow connections without verification.</para>
+ <note><para>
+ Using the same auth section for inbound and outbound
+ authentication is not recommended. There is a difference in
+ meaning for an empty realm setting between inbound and outbound
+ authentication uses. See the auth realm description for details.
+ </para></note>
+ </description>
</configOption>
<configOption name="callerid">
<synopsis>CallerID information for the endpoint</synopsis>
@@ -329,7 +335,18 @@
<synopsis>Default Music On Hold class</synopsis>
</configOption>
<configOption name="outbound_auth">
- <synopsis>Authentication object used for outbound requests</synopsis>
+ <synopsis>Authentication object(s) used for outbound requests</synopsis>
+ <description><para>
+ This is a comma-delimited list of <replaceable>auth</replaceable>
+ sections defined in <filename>pjsip.conf</filename> used to respond
+ to outbound connection authentication challenges.</para>
+ <note><para>
+ Using the same auth section for inbound and outbound
+ authentication is not recommended. There is a difference in
+ meaning for an empty realm setting between inbound and outbound
+ authentication uses. See the auth realm description for details.
+ </para></note>
+ </description>
</configOption>
<configOption name="outbound_proxy">
<synopsis>Proxy through which to send requests, a full SIP URI must be provided</synopsis>
@@ -967,8 +984,30 @@
<synopsis>PlainText password used for authentication.</synopsis>
<description><para>Only used when auth_type is <literal>userpass</literal>.</para></description>
</configOption>
- <configOption name="realm" default="asterisk">
+ <configOption name="realm">
<synopsis>SIP realm for endpoint</synopsis>
+ <description><para>
+ The treatment of this value depends upon how the authentication
+ object is used.
+ </para><para>
+ When used as an inbound authentication object, the realm is sent
+ as part of the challenge so the peer can know which key to use
+ when responding. An empty value will use the
+ <replaceable>global</replaceable> section's
+ <literal>default_realm</literal> value when issuing a challenge.
+ </para><para>
+ When used as an outbound authentication object, the realm is
+ matched with the received challenge realm to determine which
+ authentication object to use when responding to the challenge. An
+ empty value matches any challenging realm when determining
+ which authentication object matches a received challenge.
+ </para>
+ <note><para>
+ Using the same auth section for inbound and outbound
+ authentication is not recommended. There is a difference in
+ meaning for an empty realm setting between inbound and outbound
+ authentication uses.</para></note>
+ </description>
</configOption>
<configOption name="type">
<synopsis>Must be 'auth'</synopsis>
@@ -1512,7 +1551,7 @@
used.</synopsis>
</configOption>
<configOption name="default_realm" default="asterisk">
- <synopsis>When Asterisk generates an challenge, the digest will be
+ <synopsis>When Asterisk generates a challenge, the digest realm will be
set to this value if there is no better option (such as auth/realm) to be
used.</synopsis>
</configOption>
diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c
index eabfa4ba9..cca26a83c 100644
--- a/res/res_pjsip/pjsip_distributor.c
+++ b/res/res_pjsip/pjsip_distributor.c
@@ -43,7 +43,6 @@ struct ast_sched_context *prune_context;
/* From the auth/realm realtime column size */
#define MAX_REALM_LENGTH 40
-static char default_realm[MAX_REALM_LENGTH + 1];
#define DEFAULT_SUSPECTS_BUCKETS 53
@@ -120,12 +119,12 @@ static struct ast_taskprocessor *find_request_serializer(pjsip_rx_data *rdata)
tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE);
if (!tsx) {
- ast_debug(1, "Could not find %.*s transaction for %d response.\n",
- (int) pj_strlen(&rdata->msg_info.cseq->method.name),
- pj_strbuf(&rdata->msg_info.cseq->method.name),
- rdata->msg_info.msg->line.status.code);
+ ast_debug(1, "Could not find transaction for %s.\n",
+ pjsip_rx_data_get_info(rdata));
return NULL;
}
+ ast_debug(3, "Found transaction %s for %s.\n",
+ tsx->obj_name, pjsip_rx_data_get_info(rdata));
if (tsx->last_tx) {
const char *serializer_name;
@@ -402,21 +401,14 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
if (serializer) {
/* We have a serializer so we know where to send the message. */
} else if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG) {
- ast_debug(3, "No dialog serializer for response %s. Using request transaction as basis\n",
+ ast_debug(3, "No dialog serializer for %s. Using request transaction as basis.\n",
pjsip_rx_data_get_info(rdata));
serializer = find_request_serializer(rdata);
if (!serializer) {
- if (ast_taskprocessor_alert_get()) {
- /* We're overloaded, ignore the unmatched response. */
- ast_debug(3, "Taskprocessor overload alert: Ignoring unmatched '%s'.\n",
- pjsip_rx_data_get_info(rdata));
- return PJ_TRUE;
- }
-
/*
- * Pick a serializer for the unmatched response. Maybe
- * the stack can figure out what it is for, or we really
- * should just toss it regardless.
+ * Pick a serializer for the unmatched response.
+ * We couldn't determine what serializer originally
+ * sent the request or the serializer is gone.
*/
serializer = ast_sip_get_distributor_serializer(rdata);
}
@@ -462,35 +454,54 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
return PJ_TRUE;
}
-static struct ast_sip_auth *artificial_auth;
+static struct ast_sip_auth *alloc_artificial_auth(char *default_realm)
+{
+ struct ast_sip_auth *fake_auth;
+
+ fake_auth = ast_sorcery_alloc(ast_sip_get_sorcery(), SIP_SORCERY_AUTH_TYPE,
+ "artificial");
+ if (!fake_auth) {
+ return NULL;
+ }
+
+ ast_string_field_set(fake_auth, realm, default_realm);
+ ast_string_field_set(fake_auth, auth_user, "");
+ ast_string_field_set(fake_auth, auth_pass, "");
+ fake_auth->type = AST_SIP_AUTH_TYPE_ARTIFICIAL;
+
+ return fake_auth;
+}
+
+static AO2_GLOBAL_OBJ_STATIC(artificial_auth);
static int create_artificial_auth(void)
{
- if (!(artificial_auth = ast_sorcery_alloc(
- ast_sip_get_sorcery(), SIP_SORCERY_AUTH_TYPE, "artificial"))) {
+ char default_realm[MAX_REALM_LENGTH + 1];
+ struct ast_sip_auth *fake_auth;
+
+ ast_sip_get_default_realm(default_realm, sizeof(default_realm));
+ fake_auth = alloc_artificial_auth(default_realm);
+ if (!fake_auth) {
ast_log(LOG_ERROR, "Unable to create artificial auth\n");
return -1;
}
- ast_string_field_set(artificial_auth, realm, default_realm);
- ast_string_field_set(artificial_auth, auth_user, "");
- ast_string_field_set(artificial_auth, auth_pass, "");
- artificial_auth->type = AST_SIP_AUTH_TYPE_ARTIFICIAL;
+ ao2_global_obj_replace_unref(artificial_auth, fake_auth);
+ ao2_ref(fake_auth, -1);
return 0;
}
struct ast_sip_auth *ast_sip_get_artificial_auth(void)
{
- ao2_ref(artificial_auth, +1);
- return artificial_auth;
+ return ao2_global_obj_ref(artificial_auth);
}
static struct ast_sip_endpoint *artificial_endpoint = NULL;
static int create_artificial_endpoint(void)
{
- if (!(artificial_endpoint = ast_sorcery_alloc(
- ast_sip_get_sorcery(), "endpoint", NULL))) {
+ artificial_endpoint = ast_sorcery_alloc(ast_sip_get_sorcery(), "endpoint", NULL);
+ if (!artificial_endpoint) {
return -1;
}
@@ -760,7 +771,7 @@ static int distribute(void *data)
.start_mod = &distributor_mod,
.idx_after_start = 1,
};
- pj_bool_t handled;
+ pj_bool_t handled = PJ_FALSE;
pjsip_rx_data *rdata = data;
int is_request = rdata->msg_info.msg->type == PJSIP_REQUEST_MSG;
int is_ack = is_request ? rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD : 0;
@@ -968,20 +979,40 @@ static int clean_task(const void *data)
static void global_loaded(const char *object_type)
{
- char *identifier_order = ast_sip_get_endpoint_identifier_order();
- char *io_copy = identifier_order ? ast_strdupa(identifier_order) : NULL;
- char *identify_method;
-
- ast_free(identifier_order);
- using_auth_username = 0;
- while ((identify_method = ast_strip(strsep(&io_copy, ",")))) {
- if (!strcmp(identify_method, "auth_username")) {
- using_auth_username = 1;
- break;
+ char default_realm[MAX_REALM_LENGTH + 1];
+ struct ast_sip_auth *fake_auth;
+ char *identifier_order;
+
+ /* Update using_auth_username */
+ identifier_order = ast_sip_get_endpoint_identifier_order();
+ if (identifier_order) {
+ char *identify_method;
+ char *io_copy = ast_strdupa(identifier_order);
+ int new_using = 0;
+
+ ast_free(identifier_order);
+ while ((identify_method = ast_strip(strsep(&io_copy, ",")))) {
+ if (!strcmp(identify_method, "auth_username")) {
+ new_using = 1;
+ break;
+ }
}
+ using_auth_username = new_using;
}
+ /* Update default_realm of artificial_auth */
ast_sip_get_default_realm(default_realm, sizeof(default_realm));
+ fake_auth = ast_sip_get_artificial_auth();
+ if (!fake_auth || strcmp(fake_auth->realm, default_realm)) {
+ ao2_cleanup(fake_auth);
+
+ fake_auth = alloc_artificial_auth(default_realm);
+ if (fake_auth) {
+ ao2_global_obj_replace_unref(artificial_auth, fake_auth);
+ ao2_ref(fake_auth, -1);
+ }
+ }
+
ast_sip_get_unidentified_request_thresholds(&unidentified_count, &unidentified_period, &unidentified_prune_interval);
/* Clean out the old task, if any */
@@ -1114,7 +1145,7 @@ void ast_sip_destroy_distributor(void)
internal_sip_unregister_service(&endpoint_mod);
internal_sip_unregister_service(&distributor_mod);
- ao2_cleanup(artificial_auth);
+ ao2_global_obj_release(artificial_auth);
ao2_cleanup(artificial_endpoint);
ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &global_observer);
diff --git a/res/res_pjsip_authenticator_digest.c b/res/res_pjsip_authenticator_digest.c
index 4bc35c5ff..ef57e3754 100644
--- a/res/res_pjsip_authenticator_digest.c
+++ b/res/res_pjsip_authenticator_digest.c
@@ -87,46 +87,46 @@ static void auth_store_cleanup(void *data)
AST_THREADSTORAGE_CUSTOM(auth_store, NULL, auth_store_cleanup);
/*!
- * \brief Store authentication information in thread-local storage
+ * \brief Store shallow copy authentication information in thread-local storage
*/
-static int store_auth(struct ast_sip_auth *auth)
+static int store_auth(const struct ast_sip_auth *auth)
{
- struct ast_sip_auth **pointing;
+ const struct ast_sip_auth **pointing;
+
pointing = ast_threadstorage_get(&auth_store, sizeof(pointing));
- if (!pointing || *pointing) {
+ if (!pointing) {
return -1;
}
- ao2_ref(auth, +1);
*pointing = auth;
return 0;
}
/*!
- * \brief Remove authentication information from thread-local storage
+ * \brief Remove shallow copy authentication information from thread-local storage
*/
static int remove_auth(void)
{
struct ast_sip_auth **pointing;
+
pointing = ast_threadstorage_get(&auth_store, sizeof(pointing));
if (!pointing) {
return -1;
}
- ao2_cleanup(*pointing);
*pointing = NULL;
return 0;
}
/*!
- * \brief Retrieve authentication information from thread-local storage
+ * \brief Retrieve shallow copy authentication information from thread-local storage
*/
-static struct ast_sip_auth *get_auth(void)
+static const struct ast_sip_auth *get_auth(void)
{
struct ast_sip_auth **auth;
+
auth = ast_threadstorage_get(&auth_store, sizeof(auth));
- if (auth && *auth) {
- ao2_ref(*auth, +1);
+ if (auth) {
return *auth;
}
return NULL;
@@ -150,7 +150,9 @@ static struct ast_sip_auth *get_auth(void)
static pj_status_t digest_lookup(pj_pool_t *pool, const pj_str_t *realm,
const pj_str_t *acc_name, pjsip_cred_info *info)
{
- RAII_VAR(struct ast_sip_auth *, auth, get_auth(), ao2_cleanup);
+ const struct ast_sip_auth *auth;
+
+ auth = get_auth();
if (!auth) {
return PJSIP_SC_FORBIDDEN;
}
@@ -312,7 +314,7 @@ enum digest_verify_result {
* \return CMP_MATCH on successful authentication
* \return 0 on failed authentication
*/
-static int verify(struct ast_sip_auth *auth, pjsip_rx_data *rdata, pj_pool_t *pool)
+static int verify(const struct ast_sip_auth *auth, pjsip_rx_data *rdata, pj_pool_t *pool)
{
pj_status_t authed;
int response_code;
@@ -329,9 +331,7 @@ static int verify(struct ast_sip_auth *auth, pjsip_rx_data *rdata, pj_pool_t *po
setup_auth_srv(pool, &auth_server, auth->realm);
store_auth(auth);
-
authed = pjsip_auth_srv_verify(&auth_server, rdata, &response_code);
-
remove_auth();
if (authed == PJ_SUCCESS) {
@@ -389,47 +389,88 @@ static enum ast_sip_check_auth_result digest_check_auth(struct ast_sip_endpoint
pjsip_rx_data *rdata, pjsip_tx_data *tdata)
{
struct ast_sip_auth **auths;
+ struct ast_sip_auth **auths_shallow;
enum digest_verify_result *verify_res;
+ struct ast_sip_endpoint *artificial_endpoint;
enum ast_sip_check_auth_result res;
- int i;
+ int idx;
+ int is_artificial;
int failures = 0;
size_t auth_size;
- RAII_VAR(struct ast_sip_endpoint *, artificial_endpoint,
- ast_sip_get_artificial_endpoint(), ao2_cleanup);
-
auth_size = AST_VECTOR_SIZE(&endpoint->inbound_auths);
+ ast_assert(0 < auth_size);
auths = ast_alloca(auth_size * sizeof(*auths));
verify_res = ast_alloca(auth_size * sizeof(*verify_res));
- if (!auths) {
+ artificial_endpoint = ast_sip_get_artificial_endpoint();
+ if (!artificial_endpoint) {
+ /* Should not happen except possibly if we are shutting down. */
return AST_SIP_AUTHENTICATION_ERROR;
}
- if (endpoint == artificial_endpoint) {
+ is_artificial = endpoint == artificial_endpoint;
+ ao2_ref(artificial_endpoint, -1);
+ if (is_artificial) {
+ ast_assert(auth_size == 1);
auths[0] = ast_sip_get_artificial_auth();
- } else if (ast_sip_retrieve_auths(&endpoint->inbound_auths, auths)) {
- res = AST_SIP_AUTHENTICATION_ERROR;
- goto cleanup;
+ if (!auths[0]) {
+ /* Should not happen except possibly if we are shutting down. */
+ return AST_SIP_AUTHENTICATION_ERROR;
+ }
+ } else {
+ memset(auths, 0, auth_size * sizeof(*auths));
+ if (ast_sip_retrieve_auths(&endpoint->inbound_auths, auths)) {
+ res = AST_SIP_AUTHENTICATION_ERROR;
+ goto cleanup;
+ }
}
- for (i = 0; i < auth_size; ++i) {
- if (ast_strlen_zero(auths[i]->realm)) {
- ast_string_field_set(auths[i], realm, default_realm);
+ /* Setup shallow copy of auths */
+ if (ast_strlen_zero(default_realm)) {
+ auths_shallow = auths;
+ } else {
+ /*
+ * Set default realm on a shallow copy of the authentication
+ * objects that don't have a realm set.
+ */
+ auths_shallow = ast_alloca(auth_size * sizeof(*auths_shallow));
+ for (idx = 0; idx < auth_size; ++idx) {
+ if (ast_strlen_zero(auths[idx]->realm)) {
+ /*
+ * Make a shallow copy and set the default realm on it.
+ *
+ * The stack allocation is OK here. Normally this will
+ * loop one time. If you have multiple auths then you
+ * shouldn't need more auths than the normal complement
+ * of fingers and toes. Otherwise, you should check
+ * your sanity for setting up your system up that way.
+ */
+ auths_shallow[idx] = ast_alloca(sizeof(**auths_shallow));
+ memcpy(auths_shallow[idx], auths[idx], sizeof(**auths_shallow));
+ *((char **) (&auths_shallow[idx]->realm)) = default_realm;
+ ast_debug(3, "Using default realm '%s' on incoming auth '%s'.\n",
+ default_realm, ast_sorcery_object_get_id(auths_shallow[idx]));
+ } else {
+ auths_shallow[idx] = auths[idx];
+ }
}
- verify_res[i] = verify(auths[i], rdata, tdata->pool);
- if (verify_res[i] == AUTH_SUCCESS) {
+ }
+
+ for (idx = 0; idx < auth_size; ++idx) {
+ verify_res[idx] = verify(auths_shallow[idx], rdata, tdata->pool);
+ if (verify_res[idx] == AUTH_SUCCESS) {
res = AST_SIP_AUTHENTICATION_SUCCESS;
goto cleanup;
}
- if (verify_res[i] == AUTH_FAIL) {
+ if (verify_res[idx] == AUTH_FAIL) {
failures++;
}
}
- for (i = 0; i < auth_size; ++i) {
- challenge(auths[i]->realm, tdata, rdata, verify_res[i] == AUTH_STALE);
+ for (idx = 0; idx < auth_size; ++idx) {
+ challenge(auths_shallow[idx]->realm, tdata, rdata, verify_res[idx] == AUTH_STALE);
}
if (failures == auth_size) {
diff --git a/res/res_pjsip_outbound_publish.c b/res/res_pjsip_outbound_publish.c
index 87680480c..53eb6aca7 100644
--- a/res/res_pjsip_outbound_publish.c
+++ b/res/res_pjsip_outbound_publish.c
@@ -55,7 +55,18 @@
<synopsis>Expiration time for publications in seconds</synopsis>
</configOption>
<configOption name="outbound_auth" default="">
- <synopsis>Authentication object to be used for outbound publishes.</synopsis>
+ <synopsis>Authentication object(s) to be used for outbound publishes.</synopsis>
+ <description><para>
+ This is a comma-delimited list of <replaceable>auth</replaceable>
+ sections defined in <filename>pjsip.conf</filename> used to respond
+ to outbound authentication challenges.</para>
+ <note><para>
+ Using the same auth section for inbound and outbound
+ authentication is not recommended. There is a difference in
+ meaning for an empty realm setting between inbound and outbound
+ authentication uses. See the auth realm description for details.
+ </para></note>
+ </description>
</configOption>
<configOption name="outbound_proxy" default="">
<synopsis>SIP URI of the outbound proxy used to send publishes</synopsis>
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index d486ccd63..137f3a832 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -82,7 +82,18 @@
<synopsis>Maximum number of registration attempts.</synopsis>
</configOption>
<configOption name="outbound_auth" default="">
- <synopsis>Authentication object to be used for outbound registrations.</synopsis>
+ <synopsis>Authentication object(s) to be used for outbound registrations.</synopsis>
+ <description><para>
+ This is a comma-delimited list of <replaceable>auth</replaceable>
+ sections defined in <filename>pjsip.conf</filename> used to respond
+ to outbound authentication challenges.</para>
+ <note><para>
+ Using the same auth section for inbound and outbound
+ authentication is not recommended. There is a difference in
+ meaning for an empty realm setting between inbound and outbound
+ authentication uses. See the auth realm description for details.
+ </para></note>
+ </description>
</configOption>
<configOption name="outbound_proxy" default="">
<synopsis>Outbound Proxy used to send registrations</synopsis>
diff --git a/third-party/pjproject/patches/0011-r5554-svn-backport-Increase-SENDER_WIDTH-column-size.patch b/third-party/pjproject/patches/0011-r5554-svn-backport-Increase-SENDER_WIDTH-column-size.patch
new file mode 100644
index 000000000..4c53337a3
--- /dev/null
+++ b/third-party/pjproject/patches/0011-r5554-svn-backport-Increase-SENDER_WIDTH-column-size.patch
@@ -0,0 +1,77 @@
+From df1ceb301c8a17969c467e3cf00246cfc28d1732 Mon Sep 17 00:00:00 2001
+From: Richard Mudgett <rmudgett@digium.com>
+Date: Mon, 20 Feb 2017 12:19:05 -0600
+Subject: [PATCH 1/5] r5554 svn backport Increase SENDER_WIDTH column size for
+ 64-bit systems.
+
+Re #1994 (misc): Make the log's sender and thread width a compile-time configurable setting.
+
+Thanks to Richard Mudgett for the suggestion.
+---
+ pjlib/include/pj/config.h | 27 +++++++++++++++++++++++++++
+ pjlib/src/pj/log.c | 4 ++--
+ 2 files changed, 29 insertions(+), 2 deletions(-)
+
+diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h
+index 079d69b..3523f50 100644
+--- a/pjlib/include/pj/config.h
++++ b/pjlib/include/pj/config.h
+@@ -442,6 +442,33 @@
+ #endif
+
+ /**
++ * Log sender width.
++ *
++ * Default: 22 (for 64-bit machines), 14 otherwise
++ */
++#ifndef PJ_LOG_SENDER_WIDTH
++# if PJ_HAS_STDINT_H
++# include <stdint.h>
++# if (UINTPTR_MAX == 0xffffffffffffffff)
++# define PJ_LOG_SENDER_WIDTH 22
++# else
++# define PJ_LOG_SENDER_WIDTH 14
++# endif
++# else
++# define PJ_LOG_SENDER_WIDTH 14
++# endif
++#endif
++
++/**
++ * Log thread name width.
++ *
++ * Default: 12
++ */
++#ifndef PJ_LOG_THREAD_WIDTH
++# define PJ_LOG_THREAD_WIDTH 12
++#endif
++
++/**
+ * Colorfull terminal (for logging etc).
+ *
+ * Default: 1
+diff --git a/pjlib/src/pj/log.c b/pjlib/src/pj/log.c
+index 293ad46..cf7ac37 100644
+--- a/pjlib/src/pj/log.c
++++ b/pjlib/src/pj/log.c
+@@ -380,7 +380,7 @@ PJ_DEF(void) pj_log( const char *sender, int level,
+ pre += pj_utoa_pad(ptime.msec, pre, 3, '0');
+ }
+ if (log_decor & PJ_LOG_HAS_SENDER) {
+- enum { SENDER_WIDTH = 14 };
++ enum { SENDER_WIDTH = PJ_LOG_SENDER_WIDTH };
+ pj_size_t sender_len = strlen(sender);
+ if (pre!=log_buffer) *pre++ = ' ';
+ if (sender_len <= SENDER_WIDTH) {
+@@ -395,7 +395,7 @@ PJ_DEF(void) pj_log( const char *sender, int level,
+ }
+ }
+ if (log_decor & PJ_LOG_HAS_THREAD_ID) {
+- enum { THREAD_WIDTH = 12 };
++ enum { THREAD_WIDTH = PJ_LOG_THREAD_WIDTH };
+ const char *thread_name = pj_thread_get_name(pj_thread_this());
+ pj_size_t thread_len = strlen(thread_name);
+ *pre++ = ' ';
+--
+2.7.4
+
diff --git a/third-party/pjproject/patches/0012-Re-1945-misc-Don-t-trigger-SRV-complete-callback-whe.patch b/third-party/pjproject/patches/0012-Re-1945-misc-Don-t-trigger-SRV-complete-callback-whe.patch
new file mode 100644
index 000000000..e65556f22
--- /dev/null
+++ b/third-party/pjproject/patches/0012-Re-1945-misc-Don-t-trigger-SRV-complete-callback-whe.patch
@@ -0,0 +1,59 @@
+From 783de8956190c47a70ffefed56a1a2b21a62b235 Mon Sep 17 00:00:00 2001
+From: Riza Sulistyo <riza@teluu.com>
+Date: Mon, 23 Jan 2017 01:34:12 +0000
+Subject: [PATCH 2/5] Re #1945 (misc): Don't trigger SRV complete callback when
+ there is a parse error.
+
+git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@5536 74dad513-b988-da41-8d7b-12977e46ad98
+---
+ pjlib-util/src/pjlib-util/srv_resolver.c | 24 ++++++++++++++++++------
+ 1 file changed, 18 insertions(+), 6 deletions(-)
+
+diff --git a/pjlib-util/src/pjlib-util/srv_resolver.c b/pjlib-util/src/pjlib-util/srv_resolver.c
+index 8a4a599..8a2f7e1 100644
+--- a/pjlib-util/src/pjlib-util/srv_resolver.c
++++ b/pjlib-util/src/pjlib-util/srv_resolver.c
+@@ -652,6 +652,7 @@ static void dns_callback(void *user_data,
+
+ } else if (query_job->dns_state == PJ_DNS_TYPE_A) {
+ pj_bool_t is_type_a, srv_completed;
++ pj_dns_addr_record rec;
+
+ /* Clear outstanding job */
+ if (common->type == PJ_DNS_TYPE_A) {
+@@ -668,15 +669,26 @@ static void dns_callback(void *user_data,
+
+ is_type_a = (common->type == PJ_DNS_TYPE_A);
+
++ /* Parse response */
++ if (status==PJ_SUCCESS && pkt->hdr.anscount != 0) {
++ status = pj_dns_parse_addr_response(pkt, &rec);
++ if (status!=PJ_SUCCESS) {
++ char errmsg[PJ_ERR_MSG_SIZE];
++
++ PJ_LOG(4,(query_job->objname,
++ "DNS %s record parse error for '%.*s'."
++ " Err=%d (%s)",
++ (is_type_a ? "A" : "AAAA"),
++ (int)query_job->domain_part.slen,
++ query_job->domain_part.ptr,
++ status,
++ pj_strerror(status,errmsg,sizeof(errmsg)).ptr));
++ }
++ }
++
+ /* Check that we really have answer */
+ if (status==PJ_SUCCESS && pkt->hdr.anscount != 0) {
+ char addr[PJ_INET6_ADDRSTRLEN];
+- pj_dns_addr_record rec;
+-
+- /* Parse response */
+- status = pj_dns_parse_addr_response(pkt, &rec);
+- if (status != PJ_SUCCESS)
+- goto on_error;
+
+ pj_assert(rec.addr_count != 0);
+
+--
+2.7.4
+
diff --git a/third-party/pjproject/patches/0013-r5559-svn-backport-Fix-to-resolve-DNS-SRV-crashes.patch b/third-party/pjproject/patches/0013-r5559-svn-backport-Fix-to-resolve-DNS-SRV-crashes.patch
new file mode 100644
index 000000000..dc03cbc20
--- /dev/null
+++ b/third-party/pjproject/patches/0013-r5559-svn-backport-Fix-to-resolve-DNS-SRV-crashes.patch
@@ -0,0 +1,112 @@
+From d9d52f005f6d0242ea84e7c59ad6b25f052c8485 Mon Sep 17 00:00:00 2001
+From: Richard Mudgett <rmudgett@digium.com>
+Date: Mon, 20 Feb 2017 12:05:32 -0600
+Subject: [PATCH 3/5] r5559 svn backport Fix to resolve DNS SRV crashes.
+
+Re #1994 (misc): Don't try to resolve a DNS SRV query that is already considered resolved.
+Thanks to Richard Mudgett for the patch.
+
+srv_resolver.c: Don't try to send query if already considered resolved.
+
+* In resolve_hostnames() don't try to resolve a query that is already
+considered resolved.
+
+* In resolve_hostnames() fix DNS typo in comments.
+
+* In build_server_entries() move a common expression assigning to cnt
+earlier.
+
+sip_transport.c: Fix tdata object name to actually contain the pointer.
+
+It helps if the logs referencing a tdata object buffer actually have
+a name that includes the correct pointer as part of the name. Also
+since the tdata has its own pool it helps if any logs referencing the
+pool have the same name as the tdata object. This change brings tdata
+logging in line with how tsx objects are named.
+---
+ pjlib-util/src/pjlib-util/srv_resolver.c | 18 +++++++++++++-----
+ pjsip/src/pjsip/sip_transport.c | 3 ++-
+ 2 files changed, 15 insertions(+), 6 deletions(-)
+
+diff --git a/pjlib-util/src/pjlib-util/srv_resolver.c b/pjlib-util/src/pjlib-util/srv_resolver.c
+index 8a2f7e1..84ad3f6 100644
+--- a/pjlib-util/src/pjlib-util/srv_resolver.c
++++ b/pjlib-util/src/pjlib-util/srv_resolver.c
+@@ -407,8 +407,9 @@ static void build_server_entries(pj_dns_srv_async_query *query_job,
+ for (i=0; i<query_job->srv_cnt; ++i) {
+ pj_in_addr addr;
+ pj_in6_addr addr6;
++ unsigned cnt = query_job->srv[i].addr_cnt;
+
+- if (query_job->srv[i].addr_cnt != 0) {
++ if (cnt != 0) {
+ /* IP address already resolved */
+ continue;
+ }
+@@ -417,7 +418,6 @@ static void build_server_entries(pj_dns_srv_async_query *query_job,
+ pj_inet_pton(pj_AF_INET(), &query_job->srv[i].target_name,
+ &addr) == PJ_SUCCESS)
+ {
+- unsigned cnt = query_job->srv[i].addr_cnt;
+ pj_sockaddr_init(pj_AF_INET(), &query_job->srv[i].addr[cnt],
+ NULL, query_job->srv[i].port);
+ query_job->srv[i].addr[cnt].ipv4.sin_addr = addr;
+@@ -427,7 +427,6 @@ static void build_server_entries(pj_dns_srv_async_query *query_job,
+ pj_inet_pton(pj_AF_INET6(), &query_job->srv[i].target_name,
+ &addr6) == PJ_SUCCESS)
+ {
+- unsigned cnt = query_job->srv[i].addr_cnt;
+ pj_sockaddr_init(pj_AF_INET6(), &query_job->srv[i].addr[cnt],
+ NULL, query_job->srv[i].port);
+ query_job->srv[i].addr[cnt].ipv6.sin6_addr = addr6;
+@@ -480,6 +479,15 @@ static pj_status_t resolve_hostnames(pj_dns_srv_async_query *query_job)
+ for (i=0; i<query_job->srv_cnt; ++i) {
+ struct srv_target *srv = &query_job->srv[i];
+
++ if (srv->addr_cnt != 0) {
++ /*
++ * This query is already counted as resolved because of the
++ * additional records in the SRV response or the target name
++ * is an IP address exception in build_server_entries().
++ */
++ continue;
++ }
++
+ PJ_LOG(5, (query_job->objname,
+ "Starting async DNS A query_job for %.*s",
+ (int)srv->target_name.slen,
+@@ -493,7 +501,7 @@ static pj_status_t resolve_hostnames(pj_dns_srv_async_query *query_job)
+
+ status = PJ_SUCCESS;
+
+- /* Start DNA A record query */
++ /* Start DNS A record query */
+ if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY) == 0)
+ {
+ if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA) != 0) {
+@@ -511,7 +519,7 @@ static pj_status_t resolve_hostnames(pj_dns_srv_async_query *query_job)
+ &srv->common, &srv->q_a);
+ }
+
+- /* Start DNA AAAA record query */
++ /* Start DNS AAAA record query */
+ if (status == PJ_SUCCESS &&
+ (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA) != 0)
+ {
+diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
+index d672a6d..6dd14d1 100644
+--- a/pjsip/src/pjsip/sip_transport.c
++++ b/pjsip/src/pjsip/sip_transport.c
+@@ -422,7 +422,8 @@ PJ_DEF(pj_status_t) pjsip_tx_data_create( pjsip_tpmgr *mgr,
+ tdata = PJ_POOL_ZALLOC_T(pool, pjsip_tx_data);
+ tdata->pool = pool;
+ tdata->mgr = mgr;
+- pj_memcpy(tdata->obj_name, pool->obj_name, PJ_MAX_OBJ_NAME);
++ pj_ansi_snprintf(tdata->obj_name, sizeof(tdata->obj_name), "tdta%p", tdata);
++ pj_memcpy(pool->obj_name, tdata->obj_name, sizeof(pool->obj_name));
+
+ status = pj_atomic_create(tdata->pool, 0, &tdata->ref_cnt);
+ if (status != PJ_SUCCESS) {
+--
+2.7.4
+