res_pjsip: add option to enable ContactStatus event when contact is updated
[asterisk/asterisk.git] / include / asterisk / res_pjsip.h
index 6c48d2e..eabedee 100644 (file)
@@ -24,6 +24,7 @@
 #include <pjsip_simple.h>
 #include <pjsip/sip_transaction.h>
 #include <pj/timer.h>
+/* Needed for pj_sockaddr */
 #include <pjlib.h>
 
 #include "asterisk/stringfields.h"
@@ -41,8 +42,6 @@
 #include "asterisk/endpoints.h"
 /* Needed for ast_t38_ec_modes */
 #include "asterisk/udptl.h"
-/* Needed for pj_sockaddr */
-#include <pjlib.h>
 /* Needed for ast_rtp_dtls_cfg struct */
 #include "asterisk/rtp_engine.h"
 /* Needed for AST_VECTOR macro */
@@ -64,6 +63,8 @@ struct pjsip_tpselector;
 /*! \brief Maximum number of ciphers supported for a TLS transport */
 #define SIP_TLS_MAX_CIPHERS 64
 
+AST_VECTOR(ast_sip_service_route_vector, char *);
+
 /*!
  * \brief Structure for SIP transport information
  */
@@ -125,6 +126,21 @@ struct ast_sip_transport_state {
         * \since 13.18.0
         */
        struct ast_sockaddr external_media_address;
+       /*!
+        * Set when this transport is a flow of signaling to a target
+        * \since 17.0.0
+        */
+       int flow;
+       /*!
+        * The P-Preferred-Identity to use on traffic using this transport
+        * \since 17.0.0
+        */
+       char *preferred_identity;
+       /*!
+        * The Service Routes to use on traffic using this transport
+        * \since 17.0.0
+        */
+       struct ast_sip_service_route_vector *service_routes;
 };
 
 #define ast_sip_transport_is_nonlocal(transport_state, addr) \
@@ -133,7 +149,7 @@ struct ast_sip_transport_state {
 #define ast_sip_transport_is_local(transport_state, addr) \
        (transport_state->localnet && ast_apply_ha(transport_state->localnet, addr) != AST_SENSE_ALLOW)
 
-/*
+/*!
  * \brief Transport to bind to
  */
 struct ast_sip_transport {
@@ -215,6 +231,8 @@ struct ast_sip_transport {
        int allow_reload;
        /*! Automatically send requests out the same transport requests have come in on */
        int symmetric_transport;
+       /*! This is a flow to another target */
+       int flow;
 };
 
 #define SIP_SORCERY_DOMAIN_ALIAS_TYPE "domain_alias"
@@ -258,33 +276,31 @@ struct ast_sip_contact {
                AST_STRING_FIELD(user_agent);
                /*! The name of the aor this contact belongs to */
                AST_STRING_FIELD(aor);
+               /*! Asterisk Server name */
+               AST_STRING_FIELD(reg_server);
+               /*! IP-address of the Via header in REGISTER request */
+               AST_STRING_FIELD(via_addr);
+               /*! Content of the Call-ID header in REGISTER request */
+               AST_STRING_FIELD(call_id);
+               /*! The name of the endpoint that added the contact */
+               AST_STRING_FIELD(endpoint_name);
        );
        /*! Absolute time that this contact is no longer valid after */
        struct timeval expiration_time;
        /*! Frequency to send OPTIONS requests to contact. 0 is disabled. */
        unsigned int qualify_frequency;
-       /*! If true authenticate the qualify if needed */
+       /*! If true authenticate the qualify challenge response if needed */
        int authenticate_qualify;
        /*! Qualify timeout. 0 is diabled. */
        double qualify_timeout;
        /*! Endpoint that added the contact, only available in observers */
        struct ast_sip_endpoint *endpoint;
-       /*! Asterisk Server name */
-       AST_STRING_FIELD_EXTENDED(reg_server);
-       /*! IP-address of the Via header in REGISTER request */
-       AST_STRING_FIELD_EXTENDED(via_addr);
-       /* Port of the Via header in REGISTER request */
+       /*! Port of the Via header in REGISTER request */
        int via_port;
-       /*! Content of the Call-ID header in REGISTER request */
-       AST_STRING_FIELD_EXTENDED(call_id);
-       /*! The name of the endpoint that added the contact */
-       AST_STRING_FIELD_EXTENDED(endpoint_name);
        /*! If true delete the contact on Asterisk restart/boot */
        int prune_on_boot;
 };
 
-#define CONTACT_STATUS "contact_status"
-
 /*!
  * \brief Status type for a contact.
  */
@@ -307,23 +323,20 @@ enum ast_sip_contact_status_type {
  *         if available.
  */
 struct ast_sip_contact_status {
-       SORCERY_OBJECT(details);
        AST_DECLARE_STRING_FIELDS(
                /*! The original contact's URI */
                AST_STRING_FIELD(uri);
                /*! The name of the aor this contact_status belongs to */
                AST_STRING_FIELD(aor);
        );
-       /*! Current status for a contact (default - unavailable) */
-       enum ast_sip_contact_status_type status;
-       /*! The round trip start time set before sending a qualify request */
-       struct timeval rtt_start;
        /*! The round trip time in microseconds */
        int64_t rtt;
+       /*! Current status for a contact (default - unavailable) */
+       enum ast_sip_contact_status_type status;
        /*! Last status for a contact (default - unavailable) */
        enum ast_sip_contact_status_type last_status;
-       /*! TRUE if the contact was refreshed. e.g., re-registered */
-       unsigned int refresh:1;
+       /*! Name of the contact */
+       char name[0];
 };
 
 /*!
@@ -346,7 +359,7 @@ struct ast_sip_aor {
        unsigned int default_expiration;
        /*! Frequency to send OPTIONS requests to AOR contacts. 0 is disabled. */
        unsigned int qualify_frequency;
-       /*! If true authenticate the qualify if needed */
+       /*! If true authenticate the qualify challenge response if needed */
        int authenticate_qualify;
        /*! Maximum number of external contacts, 0 to disable */
        unsigned int max_contacts;
@@ -358,7 +371,7 @@ struct ast_sip_aor {
        unsigned int support_path;
        /*! Qualify timeout. 0 is diabled. */
        double qualify_timeout;
-       /* Voicemail extension to set in Message-Account */
+       /*! Voicemail extension to set in Message-Account */
        char *voicemail_extension;
 };
 
@@ -406,6 +419,8 @@ enum ast_sip_auth_type {
        AST_SIP_AUTH_TYPE_USER_PASS,
        /*! Credentials stored as an MD5 sum */
        AST_SIP_AUTH_TYPE_MD5,
+       /*! Google Oauth */
+       AST_SIP_AUTH_TYPE_GOOGLE_OAUTH,
        /*! Credentials not stored this is a fake auth */
        AST_SIP_AUTH_TYPE_ARTIFICIAL
 };
@@ -424,6 +439,12 @@ struct ast_sip_auth {
                AST_STRING_FIELD(auth_pass);
                /*! Authentication credentials in MD5 format (hash of user:realm:pass) */
                AST_STRING_FIELD(md5_creds);
+               /*! Refresh token to use for OAuth authentication */
+               AST_STRING_FIELD(refresh_token);
+               /*! Client ID to use for OAuth authentication */
+               AST_STRING_FIELD(oauth_clientid);
+               /*! Secret to use for OAuth authentication */
+               AST_STRING_FIELD(oauth_secret);
        );
        /*! The time period (in seconds) that a nonce may be reused */
        unsigned int nonce_lifetime;
@@ -443,6 +464,8 @@ enum ast_sip_endpoint_identifier_type {
        AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME = (1 << 1),
        /*! Identify based on source IP address */
        AST_SIP_ENDPOINT_IDENTIFY_BY_IP = (1 << 2),
+       /*! Identify based on arbitrary headers */
+       AST_SIP_ENDPOINT_IDENTIFY_BY_HEADER = (1 << 3),
 };
 AST_VECTOR(ast_sip_identify_by_vector, enum ast_sip_endpoint_identifier_type);
 
@@ -519,11 +542,11 @@ struct ast_sip_mwi_configuration {
                /*! Username to use when sending MWI NOTIFYs to this endpoint */
                AST_STRING_FIELD(fromuser);
        );
-       /* Should mailbox states be combined into a single notification? */
+       /*! Should mailbox states be combined into a single notification? */
        unsigned int aggregate;
-       /* Should a subscribe replace unsolicited notifies? */
+       /*! Should a subscribe replace unsolicited notifies? */
        unsigned int subscribe_replaces_unsolicited;
-       /* Voicemail extension to set in Message-Account */
+       /*! Voicemail extension to set in Message-Account */
        char *voicemail_extension;
 };
 
@@ -537,7 +560,7 @@ struct ast_sip_endpoint_subscription_configuration {
        unsigned int minexpiry;
        /*! Message waiting configuration */
        struct ast_sip_mwi_configuration mwi;
-       /* Context for SUBSCRIBE requests */
+       /*! Context for SUBSCRIBE requests */
        char context[AST_MAX_CONTEXT];
 };
 
@@ -570,6 +593,10 @@ struct ast_sip_endpoint_id_configuration {
        unsigned int rpid_immediate;
        /*! Do we add Diversion headers to applicable outgoing requests/responses? */
        unsigned int send_diversion;
+       /*! Do we accept connected line updates from this endpoint? */
+       unsigned int trust_connected_line;
+       /*! Do we send connected line updates to this endpoint? */
+       unsigned int send_connected_line;
        /*! When performing connected line update, which method should be used */
        enum ast_sip_session_refresh_method refresh_method;
 };
@@ -646,6 +673,10 @@ struct ast_sip_media_rtp_configuration {
        unsigned int timeout;
        /*! Number of seconds before terminating channel due to lack of RTP (when on hold) */
        unsigned int timeout_hold;
+       /*! Follow forked media with a different To tag */
+       unsigned int follow_early_media_fork;
+       /*! Accept updated SDPs on non-100rel 18X and 2XX responses with the same To tag */
+       unsigned int accept_multiple_sdp_answers;
 };
 
 /*!
@@ -749,6 +780,8 @@ struct ast_sip_endpoint {
                AST_STRING_FIELD(message_context);
                /*! Accountcode to auto-set on channels */
                AST_STRING_FIELD(accountcode);
+               /*! If set, we'll push incoming MWI NOTIFYs to stasis using this mailbox */
+               AST_STRING_FIELD(incoming_mwi_mailbox);
        );
        /*! Configuration for extensions */
        struct ast_sip_endpoint_extensions extensions;
@@ -810,8 +843,8 @@ struct ast_sip_endpoint {
        unsigned int refer_blind_progress;
        /*! Whether to notifies dialog-info 'early' on INUSE && RINGING state */
        unsigned int notify_early_inuse_ringing;
-       /*! If set, we'll push incoming MWI NOTIFYs to stasis using this mailbox */
-       AST_STRING_FIELD_EXTENDED(incoming_mwi_mailbox);
+       /*! Suppress Q.850 Reason headers on this endpoint */
+       unsigned int suppress_q850_reason_headers;
 };
 
 /*! URI parameter for symmetric transport */
@@ -931,9 +964,7 @@ enum ast_sip_contact_filter {
  * \retval 0 Success
  * \retval -1 Failure
  */
-#define ast_sip_register_service(module) \
-       __ast_sip_register_service(module, __FILE__, __LINE__, __PRETTY_FUNCTION__)
-int __ast_sip_register_service(pjsip_module *module, const char *file, int line, const char *func);
+int ast_sip_register_service(pjsip_module *module);
 
 /*!
  * This is the opposite of ast_sip_register_service().  Unregistering a
@@ -942,9 +973,7 @@ int __ast_sip_register_service(pjsip_module *module, const char *file, int line,
  *
  * \param module The PJSIP module to unregister
  */
-#define ast_sip_unregister_service(module) \
-       __ast_sip_unregister_service(module, __FILE__, __LINE__, __PRETTY_FUNCTION__)
-void __ast_sip_unregister_service(pjsip_module *module, const char *file, int line, const char *func);
+void ast_sip_unregister_service(pjsip_module *module);
 
 /*!
  * \brief Register a SIP authenticator
@@ -1063,7 +1092,7 @@ void *ast_sip_endpoint_alloc(const char *name);
 /*!
  * \brief Change state of a persistent endpoint.
  *
- * \param endpoint The SIP endpoint name to change state.
+ * \param endpoint_name The SIP endpoint name to change state.
  * \param state The new state
  * \retval 0 Success
  * \retval -1 Endpoint not found
@@ -1071,6 +1100,26 @@ void *ast_sip_endpoint_alloc(const char *name);
 int ast_sip_persistent_endpoint_update_state(const char *endpoint_name, enum ast_endpoint_state state);
 
 /*!
+ * \brief Publish the change of state for a contact.
+ *
+ * \param endpoint_name The SIP endpoint name.
+ * \param contact_status The contact status.
+ */
+void ast_sip_persistent_endpoint_publish_contact_state(const char *endpoint_name, const struct ast_sip_contact_status *contact_status);
+
+/*!
+ * \brief Retrieve the current status for a contact.
+ *
+ * \param contact The contact.
+ *
+ * \retval non-NULL Success
+ * \retval NULL Status information not found
+ *
+ * \note The returned contact status object is immutable.
+ */
+struct ast_sip_contact_status *ast_sip_get_contact_status(const struct ast_sip_contact *contact);
+
+/*!
  * \brief Get a pointer to the PJSIP endpoint.
  *
  * This is useful when modules have specific information they need
@@ -1409,7 +1458,7 @@ struct ast_sip_endpoint *ast_sip_get_artificial_endpoint(void);
  * the next item on the SIP socket(s) can be serviced. On incoming messages,
  * Asterisk automatically will push the request to a servant thread. When your
  * module callback is called, processing will already be in a servant. However,
- * for other PSJIP events, such as transaction state changes due to timer
+ * for other PJSIP events, such as transaction state changes due to timer
  * expirations, your module will be called into from a PJSIP thread. If you
  * are called into from a PJSIP thread, then you should push whatever processing
  * is needed to a servant as soon as possible. You can discern if you are currently
@@ -1545,28 +1594,92 @@ struct ast_sip_endpoint *ast_sip_dialog_get_endpoint(pjsip_dialog *dlg);
 int ast_sip_push_task(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data);
 
 /*!
- * \brief Push a task to SIP servants and wait for it to complete
+ * \brief Push a task to SIP servants and wait for it to complete.
  *
- * Like \ref ast_sip_push_task except that it blocks until the task completes.
+ * Like \ref ast_sip_push_task except that it blocks until the task
+ * completes.  If the current thread is a SIP servant thread then the
+ * task executes immediately.  Otherwise, the specified serializer
+ * executes the task and the current thread waits for it to complete.
  *
- * \warning \b Never use this function in a SIP servant thread. This can potentially
- * cause a deadlock. If you are in a SIP servant thread, just call your function
- * in-line.
+ * \note PJPROJECT callbacks tend to have locks already held when
+ * called.
  *
- * \warning \b Never hold locks that may be acquired by a SIP servant thread when
- * calling this function. Doing so may cause a deadlock if all SIP servant threads
- * are blocked waiting to acquire the lock while the thread holding the lock is
- * waiting for a free SIP servant thread.
+ * \warning \b Never hold locks that may be acquired by a SIP servant
+ * thread when calling this function.  Doing so may cause a deadlock
+ * if all SIP servant threads are blocked waiting to acquire the lock
+ * while the thread holding the lock is waiting for a free SIP servant
+ * thread.
  *
- * \param serializer The SIP serializer to which the task belongs. May be NULL.
+ * \warning \b Use of this function in an ao2 destructor callback is a
+ * bad idea.  You don't have control over which thread executes the
+ * destructor.  Attempting to shift execution to another thread with
+ * this function is likely to cause deadlock.
+ *
+ * \param serializer The SIP serializer to execute the task if the
+ * current thread is not a SIP servant.  NULL if any of the default
+ * serializers can be used.
  * \param sip_task The task to execute
  * \param task_data The parameter to pass to the task when it executes
- * \retval 0 Success
- * \retval -1 Failure
+ *
+ * \note The sip_task() return value may need to be distinguished from
+ * the failure to push the task.
+ *
+ * \return sip_task() return value on success.
+ * \retval -1 Failure to push the task.
+ */
+int ast_sip_push_task_wait_servant(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data);
+
+/*!
+ * \brief Push a task to SIP servants and wait for it to complete.
+ * \deprecated Replaced with ast_sip_push_task_wait_servant().
  */
 int ast_sip_push_task_synchronous(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data);
 
 /*!
+ * \brief Push a task to the serializer and wait for it to complete.
+ *
+ * Like \ref ast_sip_push_task except that it blocks until the task is
+ * completed by the specified serializer.  If the specified serializer
+ * is the current thread then the task executes immediately.
+ *
+ * \note PJPROJECT callbacks tend to have locks already held when
+ * called.
+ *
+ * \warning \b Never hold locks that may be acquired by a SIP servant
+ * thread when calling this function.  Doing so may cause a deadlock
+ * if all SIP servant threads are blocked waiting to acquire the lock
+ * while the thread holding the lock is waiting for a free SIP servant
+ * thread for the serializer to execute in.
+ *
+ * \warning \b Never hold locks that may be acquired by the serializer
+ * when calling this function.  Doing so will cause a deadlock.
+ *
+ * \warning \b Never use this function in the pjsip monitor thread (It
+ * is a SIP servant thread).  This is likely to cause a deadlock.
+ *
+ * \warning \b Use of this function in an ao2 destructor callback is a
+ * bad idea.  You don't have control over which thread executes the
+ * destructor.  Attempting to shift execution to another thread with
+ * this function is likely to cause deadlock.
+ *
+ * \param serializer The SIP serializer to execute the task.  NULL if
+ * any of the default serializers can be used.
+ * \param sip_task The task to execute
+ * \param task_data The parameter to pass to the task when it executes
+ *
+ * \note It is generally better to call
+ * ast_sip_push_task_wait_servant() if you pass NULL for the
+ * serializer parameter.
+ *
+ * \note The sip_task() return value may need to be distinguished from
+ * the failure to push the task.
+ *
+ * \return sip_task() return value on success.
+ * \retval -1 Failure to push the task.
+ */
+int ast_sip_push_task_wait_serializer(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data);
+
+/*!
  * \brief Determine if the current thread is a SIP servant thread
  *
  * \retval 0 This is not a SIP servant thread
@@ -1590,13 +1703,13 @@ enum ast_sip_scheduler_task_flags {
 
        /*!
         * Run at a fixed interval.
-        * Stop scheduling if the callback returns 0.
+        * Stop scheduling if the callback returns <= 0.
         * Any other value is ignored.
         */
        AST_SIP_SCHED_TASK_FIXED = (0 << 0),
        /*!
         * Run at a variable interval.
-        * Stop scheduling if the callback returns 0.
+        * Stop scheduling if the callback returns <= 0.
         * Any other return value is used as the new interval.
         */
        AST_SIP_SCHED_TASK_VARIABLE = (1 << 0),
@@ -1622,16 +1735,23 @@ enum ast_sip_scheduler_task_flags {
         */
        AST_SIP_SCHED_TASK_DATA_FREE = ( 1 << 3 ),
 
-       /*! \brief AST_SIP_SCHED_TASK_PERIODIC
-        * The task is scheduled at multiples of interval
+       /*!
+        * \brief The task is scheduled at multiples of interval
         * \see Interval
         */
        AST_SIP_SCHED_TASK_PERIODIC = (0 << 4),
-       /*! \brief AST_SIP_SCHED_TASK_DELAY
-        * The next invocation of the task is at last finish + interval
+       /*!
+        * \brief The next invocation of the task is at last finish + interval
         * \see Interval
         */
        AST_SIP_SCHED_TASK_DELAY = (1 << 4),
+       /*!
+        * \brief The scheduled task's events are tracked in the debug log.
+        * \details
+        * Schedule events such as scheduling, running, rescheduling, canceling,
+        * and destroying are logged about the task.
+        */
+       AST_SIP_SCHED_TASK_TRACK = (1 << 5),
 };
 
 /*!
@@ -1675,7 +1795,7 @@ struct ast_sip_sched_task;
  *
  */
 struct ast_sip_sched_task *ast_sip_schedule_task(struct ast_taskprocessor *serializer,
-       int interval, ast_sip_task sip_task, char *name, void *task_data,
+       int interval, ast_sip_task sip_task, const char *name, void *task_data,
        enum ast_sip_scheduler_task_flags flags);
 
 /*!
@@ -2446,10 +2566,8 @@ struct ast_sip_endpoint_formatter {
  * \brief Register an endpoint formatter.
  *
  * \param obj the formatter to register
- * \retval 0 Success
- * \retval -1 Failure
  */
-int ast_sip_register_endpoint_formatter(struct ast_sip_endpoint_formatter *obj);
+void ast_sip_register_endpoint_formatter(struct ast_sip_endpoint_formatter *obj);
 
 /*!
  * \brief Unregister an endpoint formatter.
@@ -2625,20 +2743,14 @@ struct ast_sip_supplement {
  * \retval 0 Success
  * \retval -1 Failure
  */
-#define ast_sip_register_supplement(supplement) \
-       __ast_sip_register_supplement(supplement, __FILE__, __LINE__, __PRETTY_FUNCTION__)
-int __ast_sip_register_supplement(struct ast_sip_supplement *supplement,
-       const char *file, int line, const char *func);
+void ast_sip_register_supplement(struct ast_sip_supplement *supplement);
 
 /*!
  * \brief Unregister a an supplement to SIP out of dialog processing
  *
  * \param supplement The supplement to unregister
  */
-#define ast_sip_unregister_supplement(supplement) \
-       __ast_sip_unregister_supplement(supplement, __FILE__, __LINE__, __PRETTY_FUNCTION__)
-void __ast_sip_unregister_supplement(struct ast_sip_supplement *supplement,
-       const char *file, int line, const char *func);
+void ast_sip_unregister_supplement(struct ast_sip_supplement *supplement);
 
 /*!
  * \brief Retrieve the global MWI taskprocessor high water alert trigger level.
@@ -2667,6 +2779,14 @@ int ast_sip_get_mwi_tps_queue_low(void);
 unsigned int ast_sip_get_mwi_disable_initial_unsolicited(void);
 
 /*!
+ * \brief Retrieve the global setting 'use_callerid_contact'.
+ * \since 13.24.0
+ *
+ * \retval non zero if CALLERID(num) is to be used as the default username in the contact
+ */
+unsigned int ast_sip_get_use_callerid_contact(void);
+
+/*!
  * \brief Retrieve the global setting 'ignore_uri_user_options'.
  * \since 13.12.0
  *
@@ -2675,6 +2795,15 @@ unsigned int ast_sip_get_mwi_disable_initial_unsolicited(void);
 unsigned int ast_sip_get_ignore_uri_user_options(void);
 
 /*!
+ * \brief Retrieve the global setting 'send_contact_status_on_update_registration'.
+ * \since 16.2.0
+ *
+ * \retval non zero if need to send AMI ContactStatus event when a contact is updated.
+ */
+unsigned int ast_sip_get_send_contact_status_on_update_registration(void);
+
+
+/*!
  * \brief Truncate the URI user field options string if enabled.
  * \since 13.12.0
  *
@@ -2762,15 +2891,6 @@ void ast_sip_get_default_realm(char *realm, size_t size);
  */
 void ast_sip_get_default_from_user(char *from_user, size_t size);
 
-/*! \brief Determines whether the res_pjsip module is loaded */
-#define CHECK_PJSIP_MODULE_LOADED()                            \
-       do {                                                    \
-               if (!ast_module_check("res_pjsip.so")           \
-                       || !ast_sip_get_pjsip_endpoint()) {     \
-                       return AST_MODULE_LOAD_DECLINE;         \
-               }                                               \
-       } while(0)
-
 /*!
  * \brief Retrieve the system keep alive interval setting.
  *
@@ -2818,7 +2938,7 @@ const char *ast_sip_get_contact_short_status_label(const enum ast_sip_contact_st
  */
 int ast_sip_failover_request(pjsip_tx_data *tdata);
 
-/*
+/*!
  * \brief Retrieve the local host address in IP form
  *
  * \param af The address family to retrieve
@@ -2880,6 +3000,8 @@ struct ao2_container *ast_sip_get_transport_states(void);
  * \param selector The selector to be populated
  * \retval 0 success
  * \retval -1 failure
+ *
+ * \note The transport selector must be unreffed using ast_sip_tpselector_unref
  */
 int ast_sip_set_tpselector_from_transport(const struct ast_sip_transport *transport, pjsip_tpselector *selector);
 
@@ -2891,10 +3013,81 @@ int ast_sip_set_tpselector_from_transport(const struct ast_sip_transport *transp
  * \param selector The selector to be populated
  * \retval 0 success
  * \retval -1 failure
+ *
+ * \note The transport selector must be unreffed using ast_sip_tpselector_unref
  */
 int ast_sip_set_tpselector_from_transport_name(const char *transport_name, pjsip_tpselector *selector);
 
 /*!
+ * \brief Unreference a pjsip_tpselector
+ * \since 17.0.0
+ *
+ * \param selector The selector to be unreffed
+ */
+void ast_sip_tpselector_unref(pjsip_tpselector *selector);
+
+/*!
+ * \brief Sets the PJSIP transport on a child transport
+ * \since 17.0.0
+ *
+ * \param transport_name The name of the transport to be updated
+ * \param transport The PJSIP transport
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_sip_transport_state_set_transport(const char *transport_name, pjsip_transport *transport);
+
+/*!
+ * \brief Sets the P-Preferred-Identity on a child transport
+ * \since 17.0.0
+ *
+ * \param transport_name The name of the transport to be set on
+ * \param identity The P-Preferred-Identity to use on requests on this transport
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_sip_transport_state_set_preferred_identity(const char *transport_name, const char *identity);
+
+/*!
+ * \brief Sets the service routes on a child transport
+ * \since 17.0.0
+ *
+ * \param transport_name The name of the transport to be set on
+ * \param service_routes A vector of service routes
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \note This assumes ownership of the service routes in both success and failure scenarios
+ */
+int ast_sip_transport_state_set_service_routes(const char *transport_name, struct ast_sip_service_route_vector *service_routes);
+
+/*!
+ * \brief Apply the configuration for a transport to an outgoing message
+ * \since 17.0.0
+ *
+ * \param transport_name The name of the transport to apply configuration from
+ * \param tdata The SIP message
+ */
+void ast_sip_message_apply_transport(const char *transport_name, pjsip_tx_data *tdata);
+
+/*!
+ * \brief Allocate a vector of service routes
+ * \since 17.0.0
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+struct ast_sip_service_route_vector *ast_sip_service_route_vector_alloc(void);
+
+/*!
+ * \brief Destroy a vector of service routes
+ * \since 17.0.0
+ *
+ * \param service_routes A vector of service routes
+ */
+void ast_sip_service_route_vector_destroy(struct ast_sip_service_route_vector *service_routes);
+
+/*!
  * \brief Set name and number information on an identity header.
  *
  * \param pool Memory pool to use for string duplication
@@ -2961,6 +3154,9 @@ int ast_sip_set_tpselector_from_ep_or_uri(const struct ast_sip_endpoint *endpoin
  * This API calls ast_sip_get_transport_name(endpoint, dlg->target) and if the result is
  * non-NULL, calls pjsip_dlg_set_transport.  If 'selector' is non-NULL, it is updated with
  * the selector used.
+ *
+ * \note
+ * It is the responsibility of the caller to unref the passed in selector if one is provided.
  */
 int ast_sip_dlg_set_transport(const struct ast_sip_endpoint *endpoint, pjsip_dialog *dlg,
        pjsip_tpselector *selector);
@@ -3004,6 +3200,18 @@ int ast_sip_str_to_dtmf(const char *dtmf_mode);
  */
 typedef void (*ast_transport_monitor_shutdown_cb)(void *data);
 
+/*!
+ * \brief Transport shutdown monitor data matcher
+ * \since 13.20.0
+ *
+ * \param a User data to compare.
+ * \param b User data to compare.
+ *
+ * \retval 1 The data objects match
+ * \retval 0 The data objects don't match
+ */
+typedef int (*ast_transport_monitor_data_matcher)(void *a, void *b);
+
 enum ast_transport_monitor_reg {
        /*! \brief Successfully registered the transport monitor */
        AST_TRANSPORT_MONITOR_REG_SUCCESS,
@@ -3020,37 +3228,59 @@ enum ast_transport_monitor_reg {
 
 /*!
  * \brief Register a reliable transport shutdown monitor callback.
- * \since 13.18.0
+ * \since 13.20.0
  *
  * \param transport Transport to monitor for shutdown.
  * \param cb Who to call when transport is shutdown.
  * \param ao2_data Data to pass with the callback.
  *
+ * \note The data object passed will have its reference count automatically
+ * incremented by this call and automatically decremented after the callback
+ * runs or when the callback is unregistered.
+ *
+ * There is no checking for duplicate registrations.
+ *
  * \return enum ast_transport_monitor_reg
  */
 enum ast_transport_monitor_reg ast_sip_transport_monitor_register(pjsip_transport *transport,
        ast_transport_monitor_shutdown_cb cb, void *ao2_data);
 
 /*!
- * \brief Unregister a reliable transport shutdown monitor callback.
- * \since 13.18.0
+ * \brief Unregister a reliable transport shutdown monitor
+ * \since 13.20.0
  *
  * \param transport Transport to monitor for shutdown.
- * \param cb Who to call when transport is shutdown.
+ * \param cb The callback that was used for the original register.
+ * \param data Data to pass to the matcher. May be NULL and does NOT need to be an ao2 object.
+ *             If NULL, all monitors with the provided callbck are unregistered.
+ * \param matches Matcher function that returns true if data matches the previously
+ *                registered data object.  If NULL, a simple pointer comparison is done.
+ *
+ * \note The data object passed into the original register will have its reference count
+ * automatically decremeneted.
  *
  * \return Nothing
  */
-void ast_sip_transport_monitor_unregister(pjsip_transport *transport, ast_transport_monitor_shutdown_cb cb);
+void ast_sip_transport_monitor_unregister(pjsip_transport *transport,
+       ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches);
 
 /*!
- * \brief Unregister monitor callback from all reliable transports.
- * \since 13.18.0
+ * \brief Unregister a transport shutdown monitor from all reliable transports
+ * \since 13.20.0
+ *
+ * \param cb The callback that was used for the original register.
+ * \param data Data to pass to the matcher. May be NULL and does NOT need to be an ao2 object.
+ *             If NULL, all monitors with the provided callbck are unregistered.
+ * \param matches Matcher function that returns true if ao2_data matches the previously
+ *                registered data object.  If NULL, a simple pointer comparison is done.
  *
- * \param cb Who to call when a transport is shutdown.
+ * \note The data object passed into the original register will have its reference count
+ * automatically decremeneted.
  *
  * \return Nothing
  */
-void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb);
+void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb,
+       void *data, ast_transport_monitor_data_matcher matches);
 
 /*! Transport state notification registration element.  */
 struct ast_sip_tpmgr_state_callback {