diff --git a/CHANGES b/CHANGES index 04e48419d6..2dd47245dc 100644 --- a/CHANGES +++ b/CHANGES @@ -19,6 +19,12 @@ res_agi EAGI provides. If not specified, it will continue to use the default signed linear (slin). +chan_pjsip +------------------ + * When dialing an endpoint directly or using the PJSIP_DIAL_CONTACTS dialplan + function any contact which is considered unreachable due to qualify being + enabled will no longer be called. + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 13.15.0 to Asterisk 13.16.0 ---------- ------------------------------------------------------------------------------ diff --git a/channels/pjsip/dialplan_functions.c b/channels/pjsip/dialplan_functions.c index 652a6b7f38..d11636ccc4 100644 --- a/channels/pjsip/dialplan_functions.c +++ b/channels/pjsip/dialplan_functions.c @@ -898,7 +898,7 @@ int pjsip_acf_dial_contacts_read(struct ast_channel *chan, const char *cmd, char if (!aor) { /* If the AOR provided is not found skip it, there may be more */ continue; - } else if (!(contacts = ast_sip_location_retrieve_aor_contacts(aor))) { + } else if (!(contacts = ast_sip_location_retrieve_aor_contacts_filtered(aor, AST_SIP_CONTACT_FILTER_REACHABLE))) { /* No contacts are available, skip it as well */ continue; } else if (!ao2_container_count(contacts)) { diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index bc3b8d4ff9..d4bd52c8f5 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -870,6 +870,17 @@ struct ast_sip_endpoint_identifier { struct ast_sip_endpoint *(*identify_endpoint)(pjsip_rx_data *rdata); }; +/*! + * \brief Contact retrieval filtering flags + */ +enum ast_sip_contact_filter { + /*! \brief Default filter flags */ + AST_SIP_CONTACT_FILTER_DEFAULT = 0, + + /*! \brief Return only reachable or unknown contacts */ + AST_SIP_CONTACT_FILTER_REACHABLE = (1 << 0), +}; + /*! * \brief Register a SIP service in Asterisk. * @@ -1055,6 +1066,18 @@ struct ast_sip_aor *ast_sip_location_retrieve_aor(const char *aor_name); */ struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct ast_sip_aor *aor); +/*! + * \brief Retrieve the first bound contact for an AOR and filter based on flags + * \since 13.16.0 + * + * \param aor Pointer to the AOR + * \param flags Filtering flags + * \retval NULL if no contacts available + * \retval non-NULL if contacts available + */ +struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact_filtered(const struct ast_sip_aor *aor, + unsigned int flags); + /*! * \brief Retrieve all contacts currently available for an AOR * @@ -1069,6 +1092,23 @@ struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct */ struct ao2_container *ast_sip_location_retrieve_aor_contacts(const struct ast_sip_aor *aor); +/*! + * \brief Retrieve all contacts currently available for an AOR and filter based on flags + * \since 13.16.0 + * + * \param aor Pointer to the AOR + * \param flags Filtering flags + * + * \retval NULL if no contacts available + * \retval non-NULL if contacts available + * + * \warning + * Since this function prunes expired contacts before returning, it holds a named write + * lock on the aor. If you already hold the lock, call ast_sip_location_retrieve_aor_contacts_nolock instead. + */ +struct ao2_container *ast_sip_location_retrieve_aor_contacts_filtered(const struct ast_sip_aor *aor, + unsigned int flags); + /*! * \brief Retrieve all contacts currently available for an AOR without locking the AOR * \since 13.9.0 @@ -1083,6 +1123,22 @@ struct ao2_container *ast_sip_location_retrieve_aor_contacts(const struct ast_si */ struct ao2_container *ast_sip_location_retrieve_aor_contacts_nolock(const struct ast_sip_aor *aor); +/*! + * \brief Retrieve all contacts currently available for an AOR without locking the AOR and filter based on flags + * \since 13.16.0 + * + * \param aor Pointer to the AOR + * \param flags Filtering flags + * + * \retval NULL if no contacts available + * \retval non-NULL if contacts available + * + * \warning + * This function should only be called if you already hold a named write lock on the aor. + */ +struct ao2_container *ast_sip_location_retrieve_aor_contacts_nolock_filtered(const struct ast_sip_aor *aor, + unsigned int flags); + /*! * \brief Retrieve the first bound contact from a list of AORs * @@ -1111,6 +1167,18 @@ struct ao2_container *ast_sip_location_retrieve_contacts_from_aor_list(const cha void ast_sip_location_retrieve_contact_and_aor_from_list(const char *aor_list, struct ast_sip_aor **aor, struct ast_sip_contact **contact); +/*! + * \brief Retrieve the first bound contact AND the AOR chosen from a list of AORs and filter based on flags + * \since 13.16.0 + * + * \param aor_list A comma-separated list of AOR names + * \param flags Filtering flags + * \param aor The chosen AOR + * \param contact The chosen contact + */ +void ast_sip_location_retrieve_contact_and_aor_from_list_filtered(const char *aor_list, unsigned int flags, + struct ast_sip_aor **aor, struct ast_sip_contact **contact); + /*! * \brief Retrieve a named contact * diff --git a/res/res_pjsip.c b/res/res_pjsip.c index e3a1244a4f..6f1c19e08c 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -3038,7 +3038,7 @@ pjsip_dialog *ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint, if (res != PJ_SUCCESS) { if (res == PJSIP_EINVALIDURI) { ast_log(LOG_ERROR, - "Endpoint '%s': Could not create dialog to invalid URI '%s'. Is endpoint registered?\n", + "Endpoint '%s': Could not create dialog to invalid URI '%s'. Is endpoint registered and reachable?\n", ast_sorcery_object_get_id(endpoint), uri); } return NULL; diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c index d8f0c58b5c..05e19f53a9 100644 --- a/res/res_pjsip/location.c +++ b/res/res_pjsip/location.c @@ -171,12 +171,36 @@ static int contact_link_static(void *obj, void *arg, int flags) return 0; } +/*! \brief Internal callback function which removes any contact which is unreachable */ +static int contact_remove_unreachable(void *obj, void *arg, int flags) +{ + struct ast_sip_contact *contact = obj; + struct ast_sip_contact_status *status; + int unreachable; + + status = ast_res_pjsip_find_or_create_contact_status(contact); + if (!status) { + return 0; + } + + unreachable = (status->status == UNAVAILABLE); + ao2_ref(status, -1); + + return unreachable ? CMP_MATCH : 0; +} + struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct ast_sip_aor *aor) +{ + return ast_sip_location_retrieve_first_aor_contact_filtered(aor, AST_SIP_CONTACT_FILTER_DEFAULT); +} + +struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact_filtered(const struct ast_sip_aor *aor, + unsigned int flags) { struct ao2_container *contacts; struct ast_sip_contact *contact = NULL; - contacts = ast_sip_location_retrieve_aor_contacts(aor); + contacts = ast_sip_location_retrieve_aor_contacts_filtered(aor, flags); if (contacts && ao2_container_count(contacts)) { /* Get the first AOR contact in the container. */ contact = ao2_callback(contacts, 0, NULL, NULL); @@ -186,6 +210,12 @@ struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct } struct ao2_container *ast_sip_location_retrieve_aor_contacts_nolock(const struct ast_sip_aor *aor) +{ + return ast_sip_location_retrieve_aor_contacts_nolock_filtered(aor, AST_SIP_CONTACT_FILTER_DEFAULT); +} + +struct ao2_container *ast_sip_location_retrieve_aor_contacts_nolock_filtered(const struct ast_sip_aor *aor, + unsigned int flags) { /* Give enough space for ^ at the beginning and ;@ at the end, since that is our object naming scheme */ char regex[strlen(ast_sorcery_object_get_id(aor)) + 4]; @@ -205,10 +235,20 @@ struct ao2_container *ast_sip_location_retrieve_aor_contacts_nolock(const struct ao2_callback(aor->permanent_contacts, OBJ_NODATA, contact_link_static, contacts); } + if (flags & AST_SIP_CONTACT_FILTER_REACHABLE) { + ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, contact_remove_unreachable, NULL); + } + return contacts; } struct ao2_container *ast_sip_location_retrieve_aor_contacts(const struct ast_sip_aor *aor) +{ + return ast_sip_location_retrieve_aor_contacts_filtered(aor, AST_SIP_CONTACT_FILTER_DEFAULT); +} + +struct ao2_container *ast_sip_location_retrieve_aor_contacts_filtered(const struct ast_sip_aor *aor, + unsigned int flags) { struct ao2_container *contacts; struct ast_named_lock *lock; @@ -219,15 +259,22 @@ struct ao2_container *ast_sip_location_retrieve_aor_contacts(const struct ast_si } ao2_lock(lock); - contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor); + contacts = ast_sip_location_retrieve_aor_contacts_nolock_filtered(aor, flags); ao2_unlock(lock); ast_named_lock_put(lock); return contacts; } + void ast_sip_location_retrieve_contact_and_aor_from_list(const char *aor_list, struct ast_sip_aor **aor, struct ast_sip_contact **contact) +{ + ast_sip_location_retrieve_contact_and_aor_from_list_filtered(aor_list, AST_SIP_CONTACT_FILTER_DEFAULT, aor, contact); +} + +void ast_sip_location_retrieve_contact_and_aor_from_list_filtered(const char *aor_list, unsigned int flags, + struct ast_sip_aor **aor, struct ast_sip_contact **contact) { char *aor_name; char *rest; @@ -247,7 +294,7 @@ void ast_sip_location_retrieve_contact_and_aor_from_list(const char *aor_list, s if (!(*aor)) { continue; } - *contact = ast_sip_location_retrieve_first_aor_contact(*aor); + *contact = ast_sip_location_retrieve_first_aor_contact_filtered(*aor, flags); /* If a valid contact is available use its URI for dialing */ if (*contact) { break; diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index 400eb6a849..1fec089f8e 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -1717,7 +1717,8 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint if (location || !contact) { location = S_OR(location, endpoint->aors); - ast_sip_location_retrieve_contact_and_aor_from_list(location, &found_aor, &found_contact); + ast_sip_location_retrieve_contact_and_aor_from_list_filtered(location, AST_SIP_CONTACT_FILTER_REACHABLE, + &found_aor, &found_contact); if (!found_contact || ast_strlen_zero(found_contact->uri)) { uri = location; } else {