diff options
-rw-r--r-- | CHANGES | 7 | ||||
-rw-r--r-- | addons/cdr_mysql.c | 1 | ||||
-rw-r--r-- | apps/app_originate.c | 32 | ||||
-rw-r--r-- | channels/chan_vpb.cc | 2 | ||||
-rw-r--r-- | configs/samples/pjsip.conf.sample | 10 | ||||
-rw-r--r-- | configs/samples/res_config_sqlite.conf.sample | 2 | ||||
-rwxr-xr-x | contrib/scripts/sip_to_pjsip/sip_to_pjsip.py | 30 | ||||
-rw-r--r-- | include/asterisk/cdr.h | 44 | ||||
-rw-r--r-- | include/asterisk/res_pjsip_session.h | 7 | ||||
-rw-r--r-- | include/asterisk/vector.h | 8 | ||||
-rw-r--r-- | main/cdr.c | 214 | ||||
-rw-r--r-- | main/tcptls.c | 3 | ||||
-rw-r--r-- | res/res_ari.c | 2 | ||||
-rw-r--r-- | res/res_pjproject.c | 2 | ||||
-rw-r--r-- | res/res_pjsip.c | 55 | ||||
-rw-r--r-- | res/res_pjsip/include/res_pjsip_private.h | 26 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_configuration.c | 18 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_message_filter.c | 24 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_session.c | 121 | ||||
-rw-r--r-- | res/res_pjsip_registrar.c | 156 | ||||
-rw-r--r-- | res/res_pjsip_session.c | 70 |
21 files changed, 579 insertions, 255 deletions
@@ -34,6 +34,13 @@ res_pjsip unsolicited MWI NOTIFY requests and make them available to other modules via the stasis message bus. + * The "remove_existing" option now allows a registration to succeed by + displacing any existing contacts that now exceed the "max_contacts" count. + Any removed contacts are the next to expire. The behaviour change is + beneficial when "rewrite_contact" is enabled and "max_contacts" is greater + than one. The removed contact is likely the old contact created by + "rewrite_contact" that the device is refreshing. + res_musiconhold ------------------ * By default, when res_musiconhold reloads or unloads, it sends a HUP signal diff --git a/addons/cdr_mysql.c b/addons/cdr_mysql.c index 667e6a2bf..07849b0c1 100644 --- a/addons/cdr_mysql.c +++ b/addons/cdr_mysql.c @@ -512,7 +512,6 @@ static int my_load_module(int reload) } else { calldate_compat = 0; } - ast_free(compat); if (res < 0) { if (reload) { diff --git a/apps/app_originate.c b/apps/app_originate.c index 88245f0a6..6093f30fd 100644 --- a/apps/app_originate.c +++ b/apps/app_originate.c @@ -110,6 +110,7 @@ static int originate_exec(struct ast_channel *chan, const char *data) char *parse; char *chantech, *chandata; int res = -1; + int continue_in_dialplan = 0; int outgoing_status = 0; unsigned int timeout = 30; static const char default_exten[] = "s"; @@ -159,6 +160,12 @@ static int originate_exec(struct ast_channel *chan, const char *data) goto return_cleanup; } + if (strcasecmp(args.type, "exten") && strcasecmp(args.type, "app")) { + ast_log(LOG_ERROR, "Incorrect type, it should be 'exten' or 'app': %s\n", + args.type); + goto return_cleanup; + } + if (!strcasecmp(args.type, "exten")) { int priority = 1; /* Initialized in case priority not specified */ const char *exten = args.arg2; @@ -177,23 +184,30 @@ static int originate_exec(struct ast_channel *chan, const char *data) ast_debug(1, "Originating call to '%s/%s' and connecting them to extension %s,%s,%d\n", chantech, chandata, args.arg1, exten, priority); - ast_pbx_outgoing_exten(chantech, cap_slin, chandata, + res = ast_pbx_outgoing_exten(chantech, cap_slin, chandata, timeout * 1000, args.arg1, exten, priority, &outgoing_status, AST_OUTGOING_WAIT, NULL, NULL, NULL, NULL, NULL, 0, NULL); - } else if (!strcasecmp(args.type, "app")) { + } else { ast_debug(1, "Originating call to '%s/%s' and connecting them to %s(%s)\n", chantech, chandata, args.arg1, S_OR(args.arg2, "")); - ast_pbx_outgoing_app(chantech, cap_slin, chandata, + res = ast_pbx_outgoing_app(chantech, cap_slin, chandata, timeout * 1000, args.arg1, args.arg2, &outgoing_status, AST_OUTGOING_WAIT, NULL, NULL, NULL, NULL, NULL, NULL); - } else { - ast_log(LOG_ERROR, "Incorrect type, it should be 'exten' or 'app': %s\n", - args.type); - goto return_cleanup; } - res = 0; + /* + * Getting here means that we have passed the various validation checks and + * have at least attempted the dial. If we have a reason (outgoing_status), + * we clear our error indicator so that we ultimately report the right thing + * to the caller. + */ + if (res && outgoing_status) { + res = 0; + } + + /* We need to exit cleanly if we've gotten this far */ + continue_in_dialplan = 1; return_cleanup: if (res) { @@ -226,7 +240,7 @@ return_cleanup: ao2_cleanup(cap_slin); ast_autoservice_stop(chan); - return res; + return continue_in_dialplan ? 0 : -1; } static int unload_module(void) diff --git a/channels/chan_vpb.cc b/channels/chan_vpb.cc index 1f4b861c1..545cb53bd 100644 --- a/channels/chan_vpb.cc +++ b/channels/chan_vpb.cc @@ -1787,7 +1787,7 @@ static int vpb_digit_end(struct ast_channel *ast, char digit, unsigned int durat ast_verb(4, "%s: vpb_digit: asked to play digit[%s]\n", p->dev, s); ast_mutex_lock(&p->play_dtmf_lock); - strncat(p->play_dtmf, s, sizeof(*p->play_dtmf) - strlen(p->play_dtmf) - 1); + strncat(p->play_dtmf, s, sizeof(p->play_dtmf) - strlen(p->play_dtmf) - 1); ast_mutex_unlock(&p->play_dtmf_lock); ast_mutex_unlock(&p->lock); diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample index 536c9f1ec..8f739d4a0 100644 --- a/configs/samples/pjsip.conf.sample +++ b/configs/samples/pjsip.conf.sample @@ -902,7 +902,13 @@ ;max_contacts=0 ; Maximum number of contacts that can bind to an AoR (default: ; "0") ;minimum_expiration=60 ; Minimum keep alive time for an AoR (default: "60") -;remove_existing=no ; Determines whether new contacts replace existing ones +;remove_existing=no ; Allow a registration to succeed by displacing any existing + ; contacts that now exceed the max_contacts count. Any + ; removed contacts are the next to expire. The behaviour is + ; beneficial when rewrite_contact is enabled and max_contacts + ; is greater than one. The removed contact is likely the old + ; contact created by rewrite_contact that the device is + ; refreshing. ; (default: "no") ;type= ; Must be of type aor (default: "") ;qualify_frequency=0 ; Interval at which to qualify an AoR (default: "0") @@ -1141,7 +1147,7 @@ ;outbound_auth= ; Authentication object(s) to be used for outbound ; publishes. - ; This is a comma-delimited list of auth sections + ; This is a comma-delimited list of auth sections ; defined in pjsip.conf used to respond to outbound ; authentication challenges. ; Using the same auth section for inbound and diff --git a/configs/samples/res_config_sqlite.conf.sample b/configs/samples/res_config_sqlite.conf.sample index 04e6ae2e7..2d14d46a3 100644 --- a/configs/samples/res_config_sqlite.conf.sample +++ b/configs/samples/res_config_sqlite.conf.sample @@ -8,4 +8,4 @@ dbfile => /var/lib/asterisk/sqlite.db ; extconfig.conf, the value given here is used. If cdr_table is omitted, CDR ; support is simply disabled. config_table => ast_config -cdr_table => ast_cdr +; cdr_table => ast_cdr diff --git a/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py b/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py index 98a5e9546..eb3aab3b8 100755 --- a/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py +++ b/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py @@ -116,6 +116,27 @@ def set_dtmfmode(key, val, section, pjsip, nmapped): set_value(key, 'none', section, pjsip, nmapped) +def setup_udptl(section, pjsip, nmapped): + """Sets values from udptl into the appropriate pjsip.conf options.""" + try: + val = sip.get(section, 't38pt_udptl')[0] + except LookupError: + try: + val = sip.get('general', 't38pt_udptl')[0] + except LookupError: + return + + ec = 'none' + if 'yes' in val: + set_value('t38_udptl', 'yes', section, pjsip, nmapped) + if 'no' in val: + set_value('t38_udptl', 'no', section, pjsip, nmapped) + if 'redundancy' in val: + ec = 'redundancy' + if 'fec' in val: + ec = 'fec' + set_value('t38_udptl_ec', ec, section, pjsip, nmapped) + def from_nat(key, val, section, pjsip, nmapped): """Sets values from nat into the appropriate pjsip.conf options.""" # nat from sip.conf can be comma separated list of values: @@ -387,6 +408,7 @@ peer_map = [ ['allow', merge_value], ['nat', from_nat], # rtp_symmetric, force_rport, # rewrite_contact + ['rtptimeout', set_value('rtp_timeout')], ['icesupport', set_value('ice_support')], ['autoframing', set_value('use_ptime')], ['outboundproxy', set_value('outbound_proxy')], @@ -1068,6 +1090,7 @@ def map_peer(sip, section, pjsip, nmapped): except LookupError: pass # key not found in sip.conf + setup_udptl(section, pjsip, nmapped) def find_non_mapped(sections, nmapped): """ @@ -1101,6 +1124,13 @@ def map_system(sip, pjsip, nmapped): except LookupError: pass + + try: + sipdebug = sip.get('general', 'sipdebug')[0] + set_value('debug', sipdebug, 'global', pjsip, nmapped, 'global') + except LookupError: + pass + try: useroption_parsing = sip.get('general', 'legacy_useroption_parsing')[0] set_value('ignore_uri_user_options', useroption_parsing, 'global', pjsip, nmapped, 'global') diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h index f752f7f9f..e10da8223 100644 --- a/include/asterisk/cdr.h +++ b/include/asterisk/cdr.h @@ -217,19 +217,19 @@ /*! \brief CDR engine settings */ enum ast_cdr_settings { - CDR_ENABLED = 1 << 0, /*< Enable CDRs */ - CDR_BATCHMODE = 1 << 1, /*< Whether or not we should dispatch CDRs in batches */ - CDR_UNANSWERED = 1 << 2, /*< Log unanswered CDRs */ - CDR_CONGESTION = 1 << 3, /*< Treat congestion as if it were a failed call */ - CDR_END_BEFORE_H_EXTEN = 1 << 4, /*< End the CDR before the 'h' extension runs */ - CDR_INITIATED_SECONDS = 1 << 5, /*< Include microseconds into the billing time */ - CDR_DEBUG = 1 << 6, /*< Enables extra debug statements */ + CDR_ENABLED = 1 << 0, /*!< Enable CDRs */ + CDR_BATCHMODE = 1 << 1, /*!< Whether or not we should dispatch CDRs in batches */ + CDR_UNANSWERED = 1 << 2, /*!< Log unanswered CDRs */ + CDR_CONGESTION = 1 << 3, /*!< Treat congestion as if it were a failed call */ + CDR_END_BEFORE_H_EXTEN = 1 << 4, /*!< End the CDR before the 'h' extension runs */ + CDR_INITIATED_SECONDS = 1 << 5, /*!< Include microseconds into the billing time */ + CDR_DEBUG = 1 << 6, /*!< Enables extra debug statements */ }; /*! \brief CDR Batch Mode settings */ enum ast_cdr_batch_mode_settings { - BATCH_MODE_SCHEDULER_ONLY = 1 << 0, /*< Don't spawn a thread to handle the batches - do it on the scheduler */ - BATCH_MODE_SAFE_SHUTDOWN = 1 << 1, /*< During safe shutdown, submit the batched CDRs */ + BATCH_MODE_SCHEDULER_ONLY = 1 << 0, /*!< Don't spawn a thread to handle the batches - do it on the scheduler */ + BATCH_MODE_SAFE_SHUTDOWN = 1 << 1, /*!< During safe shutdown, submit the batched CDRs */ }; /*! @@ -237,14 +237,14 @@ enum ast_cdr_batch_mode_settings { * state of a CDR object based on these flags. */ enum ast_cdr_options { - AST_CDR_FLAG_KEEP_VARS = (1 << 0), /*< Copy variables during the operation */ - AST_CDR_FLAG_DISABLE = (1 << 1), /*< Disable the current CDR */ - AST_CDR_FLAG_DISABLE_ALL = (3 << 1), /*< Disable the CDR and all future CDRs */ - AST_CDR_FLAG_PARTY_A = (1 << 3), /*< Set the channel as party A */ - AST_CDR_FLAG_FINALIZE = (1 << 4), /*< Finalize the current CDRs */ - AST_CDR_FLAG_SET_ANSWER = (1 << 5), /*< If the channel is answered, set the answer time to now */ - AST_CDR_FLAG_RESET = (1 << 6), /*< If set, set the start and answer time to now */ - AST_CDR_LOCK_APP = (1 << 7), /*< Prevent any further changes to the application field/data field for this CDR */ + AST_CDR_FLAG_KEEP_VARS = (1 << 0), /*!< Copy variables during the operation */ + AST_CDR_FLAG_DISABLE = (1 << 1), /*!< Disable the current CDR */ + AST_CDR_FLAG_DISABLE_ALL = (3 << 1), /*!< Disable the CDR and all future CDRs */ + AST_CDR_FLAG_PARTY_A = (1 << 3), /*!< Set the channel as party A */ + AST_CDR_FLAG_FINALIZE = (1 << 4), /*!< Finalize the current CDRs */ + AST_CDR_FLAG_SET_ANSWER = (1 << 5), /*!< If the channel is answered, set the answer time to now */ + AST_CDR_FLAG_RESET = (1 << 6), /*!< If set, set the start and answer time to now */ + AST_CDR_LOCK_APP = (1 << 7), /*!< Prevent any further changes to the application field/data field for this CDR */ }; /*! @@ -262,11 +262,11 @@ enum ast_cdr_disposition { /*! \brief The global options available for CDRs */ struct ast_cdr_config { - struct ast_flags settings; /*< CDR settings */ + struct ast_flags settings; /*!< CDR settings */ struct batch_settings { - unsigned int time; /*< Time between batches */ - unsigned int size; /*< Size to trigger a batch */ - struct ast_flags settings; /*< Settings for batches */ + unsigned int time; /*!< Time between batches */ + unsigned int size; /*!< Size to trigger a batch */ + struct ast_flags settings; /*!< Settings for batches */ } batch_settings; }; @@ -312,7 +312,7 @@ struct ast_cdr { unsigned int flags; /*! Unique Channel Identifier */ char uniqueid[AST_MAX_UNIQUEID]; - /* Linked group Identifier */ + /*! Linked group Identifier */ char linkedid[AST_MAX_UNIQUEID]; /*! User field */ char userfield[AST_MAX_USER_FIELD]; diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h index d275c256e..7d6b30229 100644 --- a/include/asterisk/res_pjsip_session.h +++ b/include/asterisk/res_pjsip_session.h @@ -544,6 +544,13 @@ int ast_sip_session_register_supplement(struct ast_sip_session_supplement *suppl void ast_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement); /*! + * \brief Add supplements to a SIP session + * + * \param session The session to initialize + */ +int ast_sip_session_add_supplements(struct ast_sip_session *session); + +/*! * \brief Alternative for ast_datastore_alloc() * * There are two major differences between this and ast_datastore_alloc() diff --git a/include/asterisk/vector.h b/include/asterisk/vector.h index 1e6fe038c..68ce13065 100644 --- a/include/asterisk/vector.h +++ b/include/asterisk/vector.h @@ -548,6 +548,14 @@ AST_VECTOR(ast_vector_int, int); #define AST_VECTOR_SIZE(vec) (vec)->current /*! + * \brief Get the maximum number of elements the vector can currently hold. + * + * \param vec Vector to query. + * \return Maximum number of elements the vector can currently hold. + */ +#define AST_VECTOR_MAX_SIZE(vec) (vec)->max + +/*! * \brief Reset vector. * * \param vec Vector to reset. diff --git a/main/cdr.c b/main/cdr.c index 4bcfc05b3..06cadcd8b 100644 --- a/main/cdr.c +++ b/main/cdr.c @@ -223,7 +223,7 @@ static int cdr_toggle_runtime_options(void); /*! \brief The configuration settings for this module */ struct module_config { - struct ast_cdr_config *general; /*< CDR global settings */ + struct ast_cdr_config *general; /*!< CDR global settings */ }; /*! \brief The container for the module configuration */ @@ -754,11 +754,7 @@ static void free_variables(struct varshead *headp) */ static void cdr_object_snapshot_copy(struct cdr_object_snapshot *dst, struct cdr_object_snapshot *src) { - if (dst->snapshot) { - ao2_t_ref(dst->snapshot, -1, "release old snapshot during copy"); - } - dst->snapshot = src->snapshot; - ao2_t_ref(dst->snapshot, +1, "bump new snapshot during copy"); + ao2_t_replace(dst->snapshot, src->snapshot, "CDR snapshot copy"); strcpy(dst->userfield, src->userfield); dst->flags = src->flags; copy_variables(&dst->variables, &src->variables); @@ -788,11 +784,11 @@ static int cdr_object_channel_hash_fn(const void *obj, const int flags) const struct cdr_object *cdr; const char *key; - switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) { - case OBJ_KEY: + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_KEY: key = obj; break; - case OBJ_POINTER: + case OBJ_SEARCH_OBJECT: cdr = obj; key = cdr->uniqueid; break; @@ -813,14 +809,14 @@ static int cdr_object_channel_cmp_fn(void *obj, void *arg, int flags) const char *right_key = arg; int cmp; - switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) { - case OBJ_POINTER: + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_OBJECT: right_key = right->uniqueid; /* Fall through */ - case OBJ_KEY: + case OBJ_SEARCH_KEY: cmp = strcmp(left->uniqueid, right_key); break; - case OBJ_PARTIAL_KEY: + case OBJ_SEARCH_PARTIAL_KEY: /* * We could also use a partial key struct containing a length * so strlen() does not get called for every comparison instead. @@ -1361,11 +1357,7 @@ static void cdr_object_swap_snapshot(struct cdr_object_snapshot *old_snapshot, struct ast_channel_snapshot *new_snapshot) { cdr_object_update_cid(old_snapshot, new_snapshot); - if (old_snapshot->snapshot) { - ao2_t_ref(old_snapshot->snapshot, -1, "Drop ref for swap"); - } - ao2_t_ref(new_snapshot, +1, "Bump ref for swap"); - old_snapshot->snapshot = new_snapshot; + ao2_t_replace(old_snapshot->snapshot, new_snapshot, "Swap CDR shapshot"); } /* BASE METHOD IMPLEMENTATIONS */ @@ -1461,7 +1453,8 @@ static int base_process_parked_channel(struct cdr_object *cdr, struct ast_parked /* SINGLE STATE */ -static void single_state_init_function(struct cdr_object *cdr) { +static void single_state_init_function(struct cdr_object *cdr) +{ cdr->start = ast_tvnow(); cdr_object_check_party_a_answer(cdr); } @@ -1574,11 +1567,10 @@ static enum process_bridge_enter_results single_state_process_bridge_enter(struc for (it_cdrs = ao2_iterator_init(bridge->channels, 0); !success && (channel_id = ao2_iterator_next(&it_cdrs)); ao2_ref(channel_id, -1)) { - RAII_VAR(struct cdr_object *, cand_cdr_master, - ao2_find(active_cdrs_by_channel, channel_id, OBJ_KEY), - ao2_cleanup); + struct cdr_object *cand_cdr_master; struct cdr_object *cand_cdr; + cand_cdr_master = ao2_find(active_cdrs_by_channel, channel_id, OBJ_SEARCH_KEY); if (!cand_cdr_master) { continue; } @@ -1600,6 +1592,7 @@ static enum process_bridge_enter_results single_state_process_bridge_enter(struc break; } ao2_unlock(cand_cdr_master); + ao2_cleanup(cand_cdr_master); } ao2_iterator_destroy(&it_cdrs); @@ -1626,11 +1619,9 @@ static int single_state_process_parking_bridge_enter(struct cdr_object *cdr, str static void dial_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot) { ast_assert(snapshot != NULL); + ast_assert(cdr->party_b.snapshot + && !strcasecmp(cdr->party_b.snapshot->name, snapshot->name)); - if (!cdr->party_b.snapshot - || strcasecmp(cdr->party_b.snapshot->name, snapshot->name)) { - return; - } cdr_object_swap_snapshot(&cdr->party_b, snapshot); /* If party B hangs up, finalize this CDR */ @@ -1724,11 +1715,10 @@ static enum process_bridge_enter_results dial_state_process_bridge_enter(struct for (it_cdrs = ao2_iterator_init(bridge->channels, 0); !success && (channel_id = ao2_iterator_next(&it_cdrs)); ao2_ref(channel_id, -1)) { - RAII_VAR(struct cdr_object *, cand_cdr_master, - ao2_find(active_cdrs_by_channel, channel_id, OBJ_KEY), - ao2_cleanup); + struct cdr_object *cand_cdr_master; struct cdr_object *cand_cdr; + cand_cdr_master = ao2_find(active_cdrs_by_channel, channel_id, OBJ_SEARCH_KEY); if (!cand_cdr_master) { continue; } @@ -1763,6 +1753,7 @@ static enum process_bridge_enter_results dial_state_process_bridge_enter(struct break; } ao2_unlock(cand_cdr_master); + ao2_cleanup(cand_cdr_master); } ao2_iterator_destroy(&it_cdrs); @@ -1826,10 +1817,9 @@ static int dialed_pending_state_process_dial_begin(struct cdr_object *cdr, struc static void bridge_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot) { - if (!cdr->party_b.snapshot - || strcasecmp(cdr->party_b.snapshot->name, snapshot->name)) { - return; - } + ast_assert(cdr->party_b.snapshot + && !strcasecmp(cdr->party_b.snapshot->name, snapshot->name)); + cdr_object_swap_snapshot(&cdr->party_b, snapshot); /* If party B hangs up, finalize this CDR */ @@ -1934,7 +1924,7 @@ static int dial_status_end(const char *dialstatus) static void handle_dial_message(void *data, struct stasis_subscription *sub, struct stasis_message *message) { RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup); - RAII_VAR(struct cdr_object *, cdr, NULL, ao2_cleanup); + struct cdr_object *cdr; struct ast_multi_channel_blob *payload = stasis_message_data(message); struct ast_channel_snapshot *caller; struct ast_channel_snapshot *peer; @@ -1948,6 +1938,10 @@ static void handle_dial_message(void *data, struct stasis_subscription *sub, str if (!peer && !caller) { return; } + if (filter_channel_snapshot(peer) || (caller && filter_channel_snapshot(caller))) { + return; + } + dial_status_blob = ast_json_object_get(ast_multi_channel_blob_get_json(payload), "dialstatus"); if (dial_status_blob) { dial_status = ast_json_string_get(dial_status_blob); @@ -1960,17 +1954,12 @@ static void handle_dial_message(void *data, struct stasis_subscription *sub, str (unsigned int)stasis_message_timestamp(message)->tv_sec, (unsigned int)stasis_message_timestamp(message)->tv_usec); - if (filter_channel_snapshot(peer) || (caller && filter_channel_snapshot(caller))) { - return; - } - /* Figure out who is running this show */ if (caller) { - cdr = ao2_find(active_cdrs_by_channel, caller->uniqueid, OBJ_KEY); + cdr = ao2_find(active_cdrs_by_channel, caller->uniqueid, OBJ_SEARCH_KEY); } else { - cdr = ao2_find(active_cdrs_by_channel, peer->uniqueid, OBJ_KEY); + cdr = ao2_find(active_cdrs_by_channel, peer->uniqueid, OBJ_SEARCH_KEY); } - if (!cdr) { ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", caller ? caller->name : peer->name); ast_assert(0); @@ -2010,15 +1999,12 @@ static void handle_dial_message(void *data, struct stasis_subscription *sub, str struct cdr_object *new_cdr; new_cdr = cdr_object_create_and_append(cdr); - if (!new_cdr) { - ao2_unlock(cdr); - return; + if (new_cdr) { + new_cdr->fn_table->process_dial_begin(new_cdr, caller, peer); } - new_cdr->fn_table->process_dial_begin(new_cdr, - caller, - peer); } ao2_unlock(cdr); + ao2_cleanup(cdr); } static int cdr_object_finalize_party_b(void *obj, void *arg, int flags) @@ -2026,6 +2012,7 @@ static int cdr_object_finalize_party_b(void *obj, void *arg, int flags) struct cdr_object *cdr = obj; struct ast_channel_snapshot *party_b = arg; struct cdr_object *it_cdr; + for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) { if (it_cdr->party_b.snapshot && !strcasecmp(it_cdr->party_b.snapshot->name, party_b->name)) { @@ -2043,6 +2030,7 @@ static int cdr_object_update_party_b(void *obj, void *arg, int flags) struct cdr_object *cdr = obj; struct ast_channel_snapshot *party_b = arg; struct cdr_object *it_cdr; + for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) { if (!it_cdr->fn_table->process_party_b) { continue; @@ -2090,13 +2078,11 @@ static int check_new_cdr_needed(struct ast_channel_snapshot *old_snapshot, */ static void handle_channel_cache_message(void *data, struct stasis_subscription *sub, struct stasis_message *message) { - RAII_VAR(struct cdr_object *, cdr, NULL, ao2_cleanup); + struct cdr_object *cdr; RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup); struct stasis_cache_update *update = stasis_message_data(message); struct ast_channel_snapshot *old_snapshot; struct ast_channel_snapshot *new_snapshot; - const char *uniqueid; - const char *name; struct cdr_object *it_cdr; ast_assert(update != NULL); @@ -2104,8 +2090,6 @@ static void handle_channel_cache_message(void *data, struct stasis_subscription old_snapshot = stasis_message_data(update->old_snapshot); new_snapshot = stasis_message_data(update->new_snapshot); - uniqueid = new_snapshot ? new_snapshot->uniqueid : old_snapshot->uniqueid; - name = new_snapshot ? new_snapshot->name : old_snapshot->name; if (filter_channel_cache_message(old_snapshot, new_snapshot)) { return; @@ -2118,19 +2102,25 @@ static void handle_channel_cache_message(void *data, struct stasis_subscription } cdr->is_root = 1; ao2_link(active_cdrs_by_channel, cdr); + } else { + const char *uniqueid; + + uniqueid = new_snapshot ? new_snapshot->uniqueid : old_snapshot->uniqueid; + cdr = ao2_find(active_cdrs_by_channel, uniqueid, OBJ_SEARCH_KEY); } /* Handle Party A */ if (!cdr) { - cdr = ao2_find(active_cdrs_by_channel, uniqueid, OBJ_KEY); - } - if (!cdr) { + const char *name; + + name = new_snapshot ? new_snapshot->name : old_snapshot->name; ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", name); ast_assert(0); } else { ao2_lock(cdr); if (new_snapshot) { int all_reject = 1; + for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) { if (!it_cdr->fn_table->process_party_a) { continue; @@ -2140,6 +2130,7 @@ static void handle_channel_cache_message(void *data, struct stasis_subscription if (all_reject && check_new_cdr_needed(old_snapshot, new_snapshot)) { /* We're not hung up and we have a new snapshot - we need a new CDR */ struct cdr_object *new_cdr; + new_cdr = cdr_object_create_and_append(cdr); if (new_cdr) { new_cdr->fn_table->process_party_a(new_cdr, new_snapshot); @@ -2165,6 +2156,7 @@ static void handle_channel_cache_message(void *data, struct stasis_subscription old_snapshot); } + ao2_cleanup(cdr); } struct bridge_leave_data { @@ -2227,9 +2219,7 @@ static void handle_bridge_leave_message(void *data, struct stasis_subscription * struct ast_channel_snapshot *channel = update->channel; RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup); - RAII_VAR(struct cdr_object *, cdr, - ao2_find(active_cdrs_by_channel, channel->uniqueid, OBJ_KEY), - ao2_cleanup); + struct cdr_object *cdr; struct cdr_object *it_cdr; struct bridge_leave_data leave_data = { .bridge = bridge, @@ -2250,6 +2240,7 @@ static void handle_bridge_leave_message(void *data, struct stasis_subscription * (unsigned int)stasis_message_timestamp(message)->tv_sec, (unsigned int)stasis_message_timestamp(message)->tv_usec); + cdr = ao2_find(active_cdrs_by_channel, channel->uniqueid, OBJ_SEARCH_KEY); if (!cdr) { ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name); ast_assert(0); @@ -2270,16 +2261,16 @@ static void handle_bridge_leave_message(void *data, struct stasis_subscription * } } ao2_unlock(cdr); - if (!left_bridge) { - return; - } - if (strcmp(bridge->subclass, "parking")) { - /* Party B */ + /* Party B */ + if (left_bridge + && strcmp(bridge->subclass, "parking")) { ao2_callback(active_cdrs_by_channel, OBJ_NODATA, - cdr_object_party_b_left_bridge_cb, - &leave_data); + cdr_object_party_b_left_bridge_cb, + &leave_data); } + + ao2_cleanup(cdr); } /*! @@ -2384,17 +2375,14 @@ static void handle_bridge_pairings(struct cdr_object *cdr, struct ast_bridge_sna it_channels = ao2_iterator_init(bridge->channels, 0); while ((channel_id = ao2_iterator_next(&it_channels))) { - RAII_VAR(struct cdr_object *, cand_cdr, - ao2_find(active_cdrs_by_channel, channel_id, OBJ_KEY), - ao2_cleanup); + struct cdr_object *cand_cdr; - if (!cand_cdr) { - ao2_ref(channel_id, -1); - continue; + cand_cdr = ao2_find(active_cdrs_by_channel, channel_id, OBJ_SEARCH_KEY); + if (cand_cdr) { + bridge_candidate_process(cdr, cand_cdr); + ao2_ref(cand_cdr, -1); } - bridge_candidate_process(cdr, cand_cdr); - ao2_ref(channel_id, -1); } ao2_iterator_destroy(&it_channels); @@ -2530,9 +2518,7 @@ static void handle_bridge_enter_message(void *data, struct stasis_subscription * struct ast_bridge_blob *update = stasis_message_data(message); struct ast_bridge_snapshot *bridge = update->bridge; struct ast_channel_snapshot *channel = update->channel; - RAII_VAR(struct cdr_object *, cdr, - ao2_find(active_cdrs_by_channel, channel->uniqueid, OBJ_KEY), - ao2_cleanup); + struct cdr_object *cdr; RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup); @@ -2549,6 +2535,7 @@ static void handle_bridge_enter_message(void *data, struct stasis_subscription * (unsigned int)stasis_message_timestamp(message)->tv_sec, (unsigned int)stasis_message_timestamp(message)->tv_usec); + cdr = ao2_find(active_cdrs_by_channel, channel->uniqueid, OBJ_SEARCH_KEY); if (!cdr) { ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name); ast_assert(0); @@ -2560,6 +2547,7 @@ static void handle_bridge_enter_message(void *data, struct stasis_subscription * } else { handle_standard_bridge_enter_message(cdr, bridge, channel); } + ao2_cleanup(cdr); } /*! @@ -2574,7 +2562,7 @@ static void handle_parked_call_message(void *data, struct stasis_subscription *s { struct ast_parked_call_payload *payload = stasis_message_data(message); struct ast_channel_snapshot *channel = payload->parkee; - RAII_VAR(struct cdr_object *, cdr, NULL, ao2_cleanup); + struct cdr_object *cdr; RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup); int unhandled = 1; @@ -2599,7 +2587,7 @@ static void handle_parked_call_message(void *data, struct stasis_subscription *s (unsigned int)stasis_message_timestamp(message)->tv_sec, (unsigned int)stasis_message_timestamp(message)->tv_usec); - cdr = ao2_find(active_cdrs_by_channel, channel->uniqueid, OBJ_KEY); + cdr = ao2_find(active_cdrs_by_channel, channel->uniqueid, OBJ_SEARCH_KEY); if (!cdr) { ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name); ast_assert(0); @@ -2616,7 +2604,9 @@ static void handle_parked_call_message(void *data, struct stasis_subscription *s if (unhandled) { /* Nothing handled the messgae - we need a new one! */ - struct cdr_object *new_cdr = cdr_object_create_and_append(cdr); + struct cdr_object *new_cdr; + + new_cdr = cdr_object_create_and_append(cdr); if (new_cdr) { /* As the new CDR is created in the single state, it is guaranteed * to have a function for the parked call message and will handle @@ -2627,6 +2617,7 @@ static void handle_parked_call_message(void *data, struct stasis_subscription *s ao2_unlock(cdr); + ao2_cleanup(cdr); } /*! @@ -2915,7 +2906,7 @@ void ast_cdr_format_var(struct ast_cdr *cdr, const char *name, char **ret, char } } -/* +/*! * \internal * \brief Callback that finds all CDRs that reference a particular channel by name */ @@ -2931,7 +2922,7 @@ static int cdr_object_select_all_by_name_cb(void *obj, void *arg, int flags) return 0; } -/* +/*! * \internal * \brief Callback that finds a CDR by channel name */ @@ -3119,15 +3110,16 @@ static struct cdr_object *cdr_object_get_by_name(const char *name) int ast_cdr_getvar(const char *channel_name, const char *name, char *value, size_t length) { - RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup); + struct cdr_object *cdr; struct cdr_object *cdr_obj; - if (!cdr) { - ast_log(AST_LOG_ERROR, "Unable to find CDR for channel %s\n", channel_name); + if (ast_strlen_zero(name)) { return 1; } - if (ast_strlen_zero(name)) { + cdr = cdr_object_get_by_name(channel_name); + if (!cdr) { + ast_log(AST_LOG_ERROR, "Unable to find CDR for channel %s\n", channel_name); return 1; } @@ -3141,18 +3133,20 @@ int ast_cdr_getvar(const char *channel_name, const char *name, char *value, size ao2_unlock(cdr); + ao2_cleanup(cdr); return 0; } int ast_cdr_serialize_variables(const char *channel_name, struct ast_str **buf, char delim, char sep) { - RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup); + struct cdr_object *cdr; struct cdr_object *it_cdr; struct ast_var_t *variable; const char *var; char workspace[256]; int total = 0, x = 0, i; + cdr = cdr_object_get_by_name(channel_name); if (!cdr) { RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup); @@ -3168,8 +3162,9 @@ int ast_cdr_serialize_variables(const char *channel_name, struct ast_str **buf, ao2_lock(cdr); for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) { - if (++x > 1) + if (++x > 1) { ast_str_append(buf, 0, "\n"); + } AST_LIST_TRAVERSE(&it_cdr->party_a.variables, variable, entries) { if (!(var = ast_var_name(variable))) { @@ -3200,6 +3195,7 @@ int ast_cdr_serialize_variables(const char *channel_name, struct ast_str **buf, } } ao2_unlock(cdr); + ao2_cleanup(cdr); return total; } @@ -3252,6 +3248,7 @@ static int cdr_object_update_party_b_userfield_cb(void *obj, void *arg, int flag struct cdr_object *cdr = obj; struct party_b_userfield_update *info = arg; struct cdr_object *it_cdr; + for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) { if (it_cdr->fn_table == &finalized_state_fn_table && it_cdr->next != NULL) { continue; @@ -3266,7 +3263,7 @@ static int cdr_object_update_party_b_userfield_cb(void *obj, void *arg, int flag void ast_cdr_setuserfield(const char *channel_name, const char *userfield) { - RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup); + struct cdr_object *cdr; struct party_b_userfield_update party_b_info = { .channel_name = channel_name, .userfield = userfield, @@ -3274,6 +3271,7 @@ void ast_cdr_setuserfield(const char *channel_name, const char *userfield) struct cdr_object *it_cdr; /* Handle Party A */ + cdr = cdr_object_get_by_name(channel_name); if (cdr) { ao2_lock(cdr); for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) { @@ -3290,6 +3288,7 @@ void ast_cdr_setuserfield(const char *channel_name, const char *userfield) cdr_object_update_party_b_userfield_cb, &party_b_info); + ao2_cleanup(cdr); } static void post_cdr(struct ast_cdr *cdr) @@ -3328,9 +3327,10 @@ static void post_cdr(struct ast_cdr *cdr) int ast_cdr_set_property(const char *channel_name, enum ast_cdr_options option) { - RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup); + struct cdr_object *cdr; struct cdr_object *it_cdr; + cdr = cdr_object_get_by_name(channel_name); if (!cdr) { return -1; } @@ -3348,14 +3348,16 @@ int ast_cdr_set_property(const char *channel_name, enum ast_cdr_options option) } ao2_unlock(cdr); + ao2_cleanup(cdr); return 0; } int ast_cdr_clear_property(const char *channel_name, enum ast_cdr_options option) { - RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup); + struct cdr_object *cdr; struct cdr_object *it_cdr; + cdr = cdr_object_get_by_name(channel_name); if (!cdr) { return -1; } @@ -3369,15 +3371,17 @@ int ast_cdr_clear_property(const char *channel_name, enum ast_cdr_options option } ao2_unlock(cdr); + ao2_cleanup(cdr); return 0; } int ast_cdr_reset(const char *channel_name, int keep_variables) { - RAII_VAR(struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup); + struct cdr_object *cdr; struct ast_var_t *vardata; struct cdr_object *it_cdr; + cdr = cdr_object_get_by_name(channel_name); if (!cdr) { return -1; } @@ -3405,6 +3409,7 @@ int ast_cdr_reset(const char *channel_name, int keep_variables) } ao2_unlock(cdr); + ao2_cleanup(cdr); return 0; } @@ -3814,7 +3819,7 @@ static void cli_show_channel(struct ast_cli_args *a) char answer_time_buffer[64]; char end_time_buffer[64]; const char *channel_name = a->argv[3]; - RAII_VAR(struct cdr_object *, cdr, NULL, ao2_cleanup); + struct cdr_object *cdr; #define TITLE_STRING "%-10.10s %-20.20s %-25.25s %-15.15s %-15.15s %-8.8s %-8.8s %-8.8s %-8.8s %-8.8s\n" #define FORMAT_STRING "%-10.10s %-20.20s %-25.25s %-15.15s %-15.15s %-8.8s %-8.8s %-8.8s %-8.8ld %-8.8ld\n" @@ -3833,6 +3838,7 @@ static void cli_show_channel(struct ast_cli_args *a) ao2_lock(cdr); for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) { struct timeval end; + if (snapshot_is_dialed(it_cdr->party_a.snapshot)) { continue; } @@ -3858,6 +3864,9 @@ static void cli_show_channel(struct ast_cli_args *a) (long)ast_tvdiff_ms(end, it_cdr->start) / 1000); } ao2_unlock(cdr); + + ao2_cleanup(cdr); + #undef FORMAT_STRING #undef TITLE_STRING } @@ -4249,8 +4258,8 @@ int ast_cdr_engine_init(void) stasis_message_router_add(stasis_router, ast_parked_call_type(), handle_parked_call_message, NULL); stasis_message_router_add(stasis_router, cdr_sync_message_type(), handle_cdr_sync_message, NULL); - active_cdrs_by_channel = ao2_container_alloc(NUM_CDR_BUCKETS, - cdr_object_channel_hash_fn, cdr_object_channel_cmp_fn); + active_cdrs_by_channel = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, + NUM_CDR_BUCKETS, cdr_object_channel_hash_fn, NULL, cdr_object_channel_cmp_fn); if (!active_cdrs_by_channel) { return -1; } @@ -4272,8 +4281,6 @@ int ast_cdr_engine_init(void) void ast_cdr_engine_term(void) { RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup); - RAII_VAR(void *, payload, NULL, ao2_cleanup); - RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); /* Since this is called explicitly during process shutdown, we might not have ever * been initialized. If so, the config object will be NULL. @@ -4283,9 +4290,16 @@ void ast_cdr_engine_term(void) } if (cdr_sync_message_type()) { + void *payload; + struct stasis_message *message; + + if (!stasis_router) { + return; + } + /* Make sure we have the needed items */ payload = ao2_alloc(sizeof(*payload), NULL); - if (!stasis_router || !payload) { + if (!payload) { return; } @@ -4295,6 +4309,8 @@ void ast_cdr_engine_term(void) if (message) { stasis_message_router_publish_sync(stasis_router, message); } + ao2_cleanup(message); + ao2_cleanup(payload); } if (ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE)) { @@ -4307,7 +4323,7 @@ int ast_cdr_engine_reload(void) RAII_VAR(struct module_config *, old_mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup); RAII_VAR(struct module_config *, mod_cfg, NULL, ao2_cleanup); - if (process_config(1)) { + if (!old_mod_cfg || process_config(1)) { return -1; } @@ -4323,5 +4339,3 @@ int ast_cdr_engine_reload(void) return cdr_toggle_runtime_options(); } - - diff --git a/main/tcptls.c b/main/tcptls.c index 0d172f149..e237dc7bb 100644 --- a/main/tcptls.c +++ b/main/tcptls.c @@ -1147,7 +1147,8 @@ struct ast_tcptls_session_instance *ast_tcptls_client_create(struct ast_tcptls_s /* if a local address was specified, bind to it so the connection will originate from the desired address */ - if (!ast_sockaddr_isnull(&desc->local_address)) { + if (!ast_sockaddr_isnull(&desc->local_address) && + !ast_sockaddr_is_any(&desc->local_address)) { setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); if (ast_bind(desc->accept_fd, &desc->local_address)) { ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n", diff --git a/res/res_ari.c b/res/res_ari.c index c6fbc6c50..d99150b7a 100644 --- a/res/res_ari.c +++ b/res/res_ari.c @@ -886,7 +886,7 @@ static int ast_ari_callback(struct ast_tcptls_session_instance *ser, RAII_VAR(struct ast_variable *, post_vars, NULL, ast_variables_destroy); struct ast_variable *var; const char *app_name = NULL; - RAII_VAR(struct ast_json *, body, ast_json_null(), ast_json_free); + RAII_VAR(struct ast_json *, body, ast_json_null(), ast_json_unref); int debug_app = 0; if (!response_body) { diff --git a/res/res_pjproject.c b/res/res_pjproject.c index 2566baf78..93694fae1 100644 --- a/res/res_pjproject.c +++ b/res/res_pjproject.c @@ -546,7 +546,7 @@ static int unload_module(void) pj_log_set_log_func(log_cb_orig); pj_log_set_decor(decor_orig); - AST_VECTOR_REMOVE_CMP_UNORDERED(&buildopts, NULL, NOT_EQUALS, ast_free); + AST_VECTOR_CALLBACK_VOID(&buildopts, ast_free); AST_VECTOR_FREE(&buildopts); ast_debug(3, "Stopped PJPROJECT logging to Asterisk logger\n"); diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 4d5c5cb83..9e436ae3c 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -1411,6 +1411,18 @@ It only limits contacts added through external interaction, such as registration. </para> + <note><para>The <replaceable>rewrite_contact</replaceable> option + registers the source address as the contact address to help with + NAT and reusing connection oriented transports such as TCP and + TLS. Unfortunately, refreshing a registration may register a + different contact address and exceed + <replaceable>max_contacts</replaceable>. The + <replaceable>remove_existing</replaceable> option can help by + removing the soonest to expire contact(s) over + <replaceable>max_contacts</replaceable> which is likely the + old <replaceable>rewrite_contact</replaceable> contact source + address being refreshed. + </para></note> <note><para>This should be set to <literal>1</literal> and <replaceable>remove_existing</replaceable> set to <literal>yes</literal> if you wish to stick with the older <literal>chan_sip</literal> behaviour. @@ -1420,15 +1432,29 @@ <configOption name="minimum_expiration" default="60"> <synopsis>Minimum keep alive time for an AoR</synopsis> <description><para> - Minimum time to keep a peer with an explict expiration. Time in seconds. + Minimum time to keep a peer with an explicit expiration. Time in seconds. </para></description> </configOption> <configOption name="remove_existing" default="no"> <synopsis>Determines whether new contacts replace existing ones.</synopsis> <description><para> - On receiving a new registration to the AoR should it remove - the existing contact that was registered against it? + On receiving a new registration to the AoR should it remove enough + existing contacts not added or updated by the registration to + satisfy <replaceable>max_contacts</replaceable>? Any removed + contacts will expire the soonest. </para> + <note><para>The <replaceable>rewrite_contact</replaceable> option + registers the source address as the contact address to help with + NAT and reusing connection oriented transports such as TCP and + TLS. Unfortunately, refreshing a registration may register a + different contact address and exceed + <replaceable>max_contacts</replaceable>. The + <replaceable>remove_existing</replaceable> option can help by + removing the soonest to expire contact(s) over + <replaceable>max_contacts</replaceable> which is likely the + old <replaceable>rewrite_contact</replaceable> contact source + address being refreshed. + </para></note> <note><para>This should be set to <literal>yes</literal> and <replaceable>max_contacts</replaceable> set to <literal>1</literal> if you wish to stick with the older <literal>chan_sip</literal> behaviour. @@ -3476,7 +3502,7 @@ int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg, AST_RWLIST_HEAD_STATIC(supplements, ast_sip_supplement); -int ast_sip_register_supplement(struct ast_sip_supplement *supplement) +void internal_sip_register_supplement(struct ast_sip_supplement *supplement) { struct ast_sip_supplement *iter; int inserted = 0; @@ -3494,22 +3520,39 @@ int ast_sip_register_supplement(struct ast_sip_supplement *supplement) if (!inserted) { AST_RWLIST_INSERT_TAIL(&supplements, supplement, next); } +} + +int ast_sip_register_supplement(struct ast_sip_supplement *supplement) +{ + internal_sip_register_supplement(supplement); ast_module_ref(ast_module_info->self); + return 0; } -void ast_sip_unregister_supplement(struct ast_sip_supplement *supplement) +int internal_sip_unregister_supplement(struct ast_sip_supplement *supplement) { struct ast_sip_supplement *iter; SCOPED_LOCK(lock, &supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK); + int res = -1; + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&supplements, iter, next) { if (supplement == iter) { AST_RWLIST_REMOVE_CURRENT(next); - ast_module_unref(ast_module_info->self); + res = 0; break; } } AST_RWLIST_TRAVERSE_SAFE_END; + + return res; +} + +void ast_sip_unregister_supplement(struct ast_sip_supplement *supplement) +{ + if (!internal_sip_unregister_supplement(supplement)) { + ast_module_unref(ast_module_info->self); + } } static int send_in_dialog_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg) diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h index 9fd7aed97..5ce3c6faf 100644 --- a/res/res_pjsip/include/res_pjsip_private.h +++ b/res/res_pjsip/include/res_pjsip_private.h @@ -328,6 +328,18 @@ int internal_sip_unregister_service(pjsip_module *module); /*! * \internal + * \brief Used by res_pjsip.so to register a supplement without adding a self reference + */ +void internal_sip_register_supplement(struct ast_sip_supplement *supplement); + +/*! + * \internal + * \brief Used by res_pjsip.so to unregister a supplement without removing a self reference + */ +int internal_sip_unregister_supplement(struct ast_sip_supplement *supplement); + +/*! + * \internal * \brief Used by res_pjsip.so to register an endpoint formatter without adding a self reference */ void internal_sip_register_endpoint_formatter(struct ast_sip_endpoint_formatter *obj); @@ -338,6 +350,20 @@ void internal_sip_register_endpoint_formatter(struct ast_sip_endpoint_formatter */ int internal_sip_unregister_endpoint_formatter(struct ast_sip_endpoint_formatter *obj); +struct ast_sip_session_supplement; + +/*! + * \internal + * \brief Used by res_pjsip.so to register a session supplement without adding a self reference + */ +void internal_sip_session_register_supplement(struct ast_sip_session_supplement *supplement); + +/*! + * \internal + * \brief Used by res_pjsip.so to unregister a session supplement without removing a self reference + */ +int internal_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement); + /*! * \internal * \brief Finds or creates contact_status for a contact diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 437476631..a39102305 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -1314,6 +1314,14 @@ static struct ast_endpoint *persistent_endpoint_find_or_create(const struct ast_ ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_OFFLINE); ao2_link_flags(persistent_endpoints, persistent, OBJ_NOLOCK); + } else if (strcmp(persistent->aors, endpoint->aors)) { + char *new_aors = ast_strdup(endpoint->aors); + + /* make sure we don't NULL persistent->aors if allocation fails. */ + if (new_aors) { + ast_free(persistent->aors); + persistent->aors = new_aors; + } } ao2_ref(persistent->endpoint, +1); @@ -1790,20 +1798,12 @@ static struct ast_cli_entry cli_commands[] = { struct ast_sip_cli_formatter_entry *channel_formatter; struct ast_sip_cli_formatter_entry *endpoint_formatter; -static int on_load_endpoint(void *obj, void *arg, int flags) -{ - return sip_endpoint_apply_handler(sip_sorcery, obj); -} - static void load_all_endpoints(void) { struct ao2_container *endpoints; endpoints = ast_sorcery_retrieve_by_fields(sip_sorcery, "endpoint", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); - if (endpoints) { - ao2_callback(endpoints, OBJ_NODATA, on_load_endpoint, NULL); - ao2_ref(endpoints, -1); - } + ao2_cleanup(endpoints); } int ast_res_pjsip_initialize_configuration(void) diff --git a/res/res_pjsip/pjsip_message_filter.c b/res/res_pjsip/pjsip_message_filter.c index d2f9b9562..978aeb070 100644 --- a/res/res_pjsip/pjsip_message_filter.c +++ b/res/res_pjsip/pjsip_message_filter.c @@ -505,32 +505,24 @@ static pj_bool_t filter_on_rx_message(pjsip_rx_data *rdata) void ast_res_pjsip_cleanup_message_filter(void) { - ast_sip_unregister_service(&filter_module_tsx); - ast_sip_unregister_service(&filter_module_transport); - ast_sip_unregister_supplement(&filter_supplement); - ast_sip_session_unregister_supplement(&filter_session_supplement); + internal_sip_unregister_service(&filter_module_tsx); + internal_sip_unregister_service(&filter_module_transport); + internal_sip_unregister_supplement(&filter_supplement); + internal_sip_session_unregister_supplement(&filter_session_supplement); } int ast_res_pjsip_init_message_filter(void) { - if (ast_sip_session_register_supplement(&filter_session_supplement)) { - ast_log(LOG_ERROR, "Could not register message filter session supplement for outgoing requests\n"); - return -1; - } - - if (ast_sip_register_supplement(&filter_supplement)) { - ast_log(LOG_ERROR, "Could not register message filter supplement for outgoing requests\n"); - ast_res_pjsip_cleanup_message_filter(); - return -1; - } + internal_sip_session_register_supplement(&filter_session_supplement); + internal_sip_register_supplement(&filter_supplement); - if (ast_sip_register_service(&filter_module_transport)) { + if (internal_sip_register_service(&filter_module_transport)) { ast_log(LOG_ERROR, "Could not register message filter module for incoming and outgoing requests\n"); ast_res_pjsip_cleanup_message_filter(); return -1; } - if (ast_sip_register_service(&filter_module_tsx)) { + if (internal_sip_register_service(&filter_module_tsx)) { ast_log(LOG_ERROR, "Could not register message filter module for incoming and outgoing requests\n"); ast_res_pjsip_cleanup_message_filter(); return -1; diff --git a/res/res_pjsip/pjsip_session.c b/res/res_pjsip/pjsip_session.c new file mode 100644 index 000000000..cea72436a --- /dev/null +++ b/res/res_pjsip/pjsip_session.c @@ -0,0 +1,121 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2017, CFWare, LLC + * + * Corey Farrell <git@cfware.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include "asterisk.h" + +#include <pjsip.h> +#include <pjsip_ua.h> +#include <pjlib.h> + +#include "asterisk/res_pjsip.h" +#include "asterisk/res_pjsip_session.h" +#include "include/res_pjsip_private.h" +#include "asterisk/linkedlists.h" +#include "asterisk/lock.h" +#include "asterisk/module.h" + + +AST_RWLIST_HEAD_STATIC(session_supplements, ast_sip_session_supplement); + +void internal_sip_session_register_supplement(struct ast_sip_session_supplement *supplement) +{ + struct ast_sip_session_supplement *iter; + int inserted = 0; + SCOPED_LOCK(lock, &session_supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK); + + if (!supplement->response_priority) { + supplement->response_priority = AST_SIP_SESSION_BEFORE_MEDIA; + } + + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&session_supplements, iter, next) { + if (iter->priority > supplement->priority) { + AST_RWLIST_INSERT_BEFORE_CURRENT(supplement, next); + inserted = 1; + break; + } + } + AST_RWLIST_TRAVERSE_SAFE_END; + + if (!inserted) { + AST_RWLIST_INSERT_TAIL(&session_supplements, supplement, next); + } +} + +int ast_sip_session_register_supplement(struct ast_sip_session_supplement *supplement) +{ + internal_sip_session_register_supplement(supplement); + ast_module_ref(AST_MODULE_SELF); + + return 0; +} + +int internal_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement) +{ + struct ast_sip_session_supplement *iter; + int res = -1; + SCOPED_LOCK(lock, &session_supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK); + + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&session_supplements, iter, next) { + if (supplement == iter) { + AST_RWLIST_REMOVE_CURRENT(next); + res = 0; + break; + } + } + AST_RWLIST_TRAVERSE_SAFE_END; + + return res; +} + +void ast_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement) +{ + if (!internal_sip_session_unregister_supplement(supplement)) { + ast_module_unref(AST_MODULE_SELF); + } +} + +static struct ast_sip_session_supplement *supplement_dup(const struct ast_sip_session_supplement *src) +{ + struct ast_sip_session_supplement *dst = ast_calloc(1, sizeof(*dst)); + + if (!dst) { + return NULL; + } + /* Will need to revisit if shallow copy becomes an issue */ + *dst = *src; + + return dst; +} + +int ast_sip_session_add_supplements(struct ast_sip_session *session) +{ + struct ast_sip_session_supplement *iter; + SCOPED_LOCK(lock, &session_supplements, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK); + + AST_RWLIST_TRAVERSE(&session_supplements, iter, next) { + struct ast_sip_session_supplement *copy = supplement_dup(iter); + + if (!copy) { + return -1; + } + AST_LIST_INSERT_TAIL(&session->supplements, copy, next); + } + + return 0; +} + diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c index ba1c074b3..32906011a 100644 --- a/res/res_pjsip_registrar.c +++ b/res/res_pjsip_registrar.c @@ -129,7 +129,8 @@ static int registrar_find_contact(void *obj, void *arg, int flags) /*! \brief Internal function which validates provided Contact headers to confirm that they are acceptable, and returns number of contacts */ static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_container *contacts, struct ast_sip_aor *aor, int *added, int *updated, int *deleted) { - pjsip_contact_hdr *previous = NULL, *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr; + pjsip_contact_hdr *previous = NULL; + pjsip_contact_hdr *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr; struct registrar_contact_details details = { .pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256), }; @@ -140,15 +141,18 @@ static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_co while ((contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) { int expiration = registrar_get_expiration(aor, contact, rdata); - RAII_VAR(struct ast_sip_contact *, existing, NULL, ao2_cleanup); + struct ast_sip_contact *existing; char contact_uri[pjsip_max_url_size]; if (contact->star) { /* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */ - if ((expiration != 0) || previous) { + if (expiration != 0 || previous) { pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return -1; } + /* Count all contacts to delete */ + *deleted = ao2_container_count(contacts); + previous = contact; continue; } else if (previous && previous->star) { /* If there is a previous contact and it is a '*' this is a deal breaker */ @@ -177,14 +181,16 @@ static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_co } /* Determine if this is an add, update, or delete for policy enforcement purposes */ - if (!(existing = ao2_callback(contacts, 0, registrar_find_contact, &details))) { + existing = ao2_callback(contacts, 0, registrar_find_contact, &details); + ao2_cleanup(existing); + if (!existing) { if (expiration) { - (*added)++; + ++*added; } } else if (expiration) { - (*updated)++; + ++*updated; } else { - (*deleted)++; + ++*deleted; } } @@ -219,7 +225,7 @@ static int registrar_delete_contact(void *obj, void *arg, int flags) contact->user_agent); } - return 0; + return CMP_MATCH; } /*! \brief Internal function which adds a contact to a response */ @@ -351,6 +357,96 @@ static void register_contact_transport_shutdown_cb(void *data) ao2_ref(aor, -1); } +AST_VECTOR(excess_contact_vector, struct ast_sip_contact *); + +static int vec_contact_cmp(struct ast_sip_contact *left, struct ast_sip_contact *right) +{ + struct ast_sip_contact *left_contact = left; + struct ast_sip_contact *right_contact = right; + + /* Sort from soonest to expire to last to expire */ + return ast_tvcmp(left_contact->expiration_time, right_contact->expiration_time); +} + +static int vec_contact_add(void *obj, void *arg, int flags) +{ + struct ast_sip_contact *contact = obj; + struct excess_contact_vector *contact_vec = arg; + + /* + * Performance wise, an insertion sort is fine because we + * shouldn't need to remove more than a handful of contacts. + * I expect we'll typically be removing only one contact. + */ + AST_VECTOR_ADD_SORTED(contact_vec, contact, vec_contact_cmp); + if (AST_VECTOR_SIZE(contact_vec) == AST_VECTOR_MAX_SIZE(contact_vec)) { + /* + * We added a contact over the number we need to remove. + * Remove the longest to expire contact from the vector + * which is the last element in the vector. It may be + * the one we just added or the one we just added pushed + * out an earlier contact from removal consideration. + */ + --AST_VECTOR_SIZE(contact_vec); + } + return 0; +} + +/*! + * \internal + * \brief Remove excess existing contacts that expire the soonest. + * \since 13.18.0 + * + * \param contacts Container of unmodified contacts that could remove. + * \param to_remove Maximum number of contacts to remove. + * + * \return Nothing + */ +static void remove_excess_contacts(struct ao2_container *contacts, unsigned int to_remove) +{ + struct excess_contact_vector contact_vec; + + /* + * Create a sorted vector to hold the to_remove soonest to + * expire contacts. The vector has an extra space to + * temporarily hold the longest to expire contact that we + * won't remove. + */ + if (AST_VECTOR_INIT(&contact_vec, to_remove + 1)) { + return; + } + ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, vec_contact_add, &contact_vec); + + /* + * The vector should always be populated with the number + * of contacts we need to remove. Just in case, we will + * remove all contacts in the vector even if the contacts + * container had fewer contacts than there should be. + */ + ast_assert(AST_VECTOR_SIZE(&contact_vec) == to_remove); + to_remove = AST_VECTOR_SIZE(&contact_vec); + + /* Remove the excess contacts that expire the soonest */ + while (to_remove--) { + struct ast_sip_contact *contact; + + contact = AST_VECTOR_GET(&contact_vec, to_remove); + + ast_sip_location_delete_contact(contact); + ast_verb(3, "Removed contact '%s' from AOR '%s' due to remove_existing\n", + contact->uri, contact->aor); + ast_test_suite_event_notify("AOR_CONTACT_REMOVED", + "Contact: %s\r\n" + "AOR: %s\r\n" + "UserAgent: %s", + contact->uri, + contact->aor, + contact->user_agent); + } + + AST_VECTOR_FREE(&contact_vec); +} + static int register_aor_core(pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint, struct ast_sip_aor *aor, @@ -359,7 +455,10 @@ static int register_aor_core(pjsip_rx_data *rdata, { static const pj_str_t USER_AGENT = { "User-Agent", 10 }; - int added = 0, updated = 0, deleted = 0; + int added = 0; + int updated = 0; + int deleted = 0; + int contact_count; pjsip_contact_hdr *contact_hdr = NULL; struct registrar_contact_details details = { 0, }; pjsip_tx_data *tdata; @@ -396,7 +495,14 @@ static int register_aor_core(pjsip_rx_data *rdata, return PJ_TRUE; } - if ((MAX(added - deleted, 0) + (!aor->remove_existing ? ao2_container_count(contacts) : 0)) > aor->max_contacts) { + if (aor->remove_existing) { + /* Cumulative number of contacts affected by this registration */ + contact_count = MAX(updated + added - deleted, 0); + } else { + /* Total contacts after this registration */ + contact_count = ao2_container_count(contacts) + added - deleted; + } + 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"); @@ -405,7 +511,9 @@ static int register_aor_core(pjsip_rx_data *rdata, return PJ_TRUE; } - if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) { + 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; } @@ -446,7 +554,8 @@ static int register_aor_core(pjsip_rx_data *rdata, if (contact_hdr->star) { /* A star means to unregister everything, so do so for the possible contacts */ - ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name); + ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, + registrar_delete_contact, (void *)aor_name); break; } @@ -459,7 +568,8 @@ static int register_aor_core(pjsip_rx_data *rdata, details.uri = pjsip_uri_get_uri(contact_hdr->uri); pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)); - if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) { + contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details); + if (!contact) { int prune_on_boot = 0; pj_str_t host_name; @@ -600,15 +710,29 @@ static int register_aor_core(pjsip_rx_data *rdata, pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); - /* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER - * do so + /* + * If the AOR is configured to remove any contacts over max_contacts + * that have not been updated/added/deleted as a result of this + * REGISTER do so. + * + * The contacts container currently holds the existing contacts that + * were not affected by this REGISTER. */ if (aor->remove_existing) { - ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL); + /* Total contacts after this registration */ + contact_count = ao2_container_count(contacts) + updated + added; + if (contact_count > aor->max_contacts) { + /* Remove excess existing contacts that expire the soonest */ + remove_excess_contacts(contacts, contact_count - aor->max_contacts); + } } /* 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_contact = ao2_callback(contacts, 0, NULL, NULL); /* Send a response containing all of the contacts (including static) that are present on this AOR */ diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index 496c4763a..f84d60e14 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -396,59 +396,6 @@ static int handle_negotiated_sdp(struct ast_sip_session *session, const pjmedia_ return -1; } -AST_RWLIST_HEAD_STATIC(session_supplements, ast_sip_session_supplement); - -int ast_sip_session_register_supplement(struct ast_sip_session_supplement *supplement) -{ - struct ast_sip_session_supplement *iter; - int inserted = 0; - SCOPED_LOCK(lock, &session_supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK); - - if (!supplement->response_priority) { - supplement->response_priority = AST_SIP_SESSION_BEFORE_MEDIA; - } - - AST_RWLIST_TRAVERSE_SAFE_BEGIN(&session_supplements, iter, next) { - if (iter->priority > supplement->priority) { - AST_RWLIST_INSERT_BEFORE_CURRENT(supplement, next); - inserted = 1; - break; - } - } - AST_RWLIST_TRAVERSE_SAFE_END; - - if (!inserted) { - AST_RWLIST_INSERT_TAIL(&session_supplements, supplement, next); - } - ast_module_ref(ast_module_info->self); - return 0; -} - -void ast_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement) -{ - struct ast_sip_session_supplement *iter; - SCOPED_LOCK(lock, &session_supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK); - AST_RWLIST_TRAVERSE_SAFE_BEGIN(&session_supplements, iter, next) { - if (supplement == iter) { - AST_RWLIST_REMOVE_CURRENT(next); - ast_module_unref(ast_module_info->self); - break; - } - } - AST_RWLIST_TRAVERSE_SAFE_END; -} - -static struct ast_sip_session_supplement *supplement_dup(const struct ast_sip_session_supplement *src) -{ - struct ast_sip_session_supplement *dst = ast_calloc(1, sizeof(*dst)); - if (!dst) { - return NULL; - } - /* Will need to revisit if shallow copy becomes an issue */ - *dst = *src; - return dst; -} - #define DATASTORE_BUCKETS 53 #define MEDIA_BUCKETS 7 @@ -1380,21 +1327,6 @@ static void session_destructor(void *obj) ast_test_suite_event_notify("SESSION_DESTROYED", "Endpoint: %s", endpoint_name); } -static int add_supplements(struct ast_sip_session *session) -{ - struct ast_sip_session_supplement *iter; - SCOPED_LOCK(lock, &session_supplements, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK); - - AST_RWLIST_TRAVERSE(&session_supplements, iter, next) { - struct ast_sip_session_supplement *copy = supplement_dup(iter); - if (!copy) { - return -1; - } - AST_LIST_INSERT_TAIL(&session->supplements, copy, next); - } - return 0; -} - static int add_session_media(void *obj, void *arg, int flags) { struct sdp_handler_list *handler_list = obj; @@ -1523,7 +1455,7 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint, session->dtmf = endpoint->dtmf; - if (add_supplements(session)) { + if (ast_sip_session_add_supplements(session)) { /* Release the ref held by session->inv_session */ ao2_ref(session, -1); return NULL; |