2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2013, Digium, Inc.
6 * Joshua Colp <jcolp@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
20 <depend>pjproject</depend>
21 <depend>res_pjsip</depend>
22 <support_level>core</support_level>
30 #include "asterisk/res_pjsip.h"
31 #include "asterisk/res_pjsip_cli.h"
32 #include "asterisk/module.h"
33 #include "asterisk/taskprocessor.h"
34 #include "asterisk/cli.h"
35 #include "asterisk/stasis_system.h"
36 #include "asterisk/threadstorage.h"
37 #include "asterisk/threadpool.h"
38 #include "asterisk/statsd.h"
39 #include "res_pjsip/include/res_pjsip_private.h"
42 <configInfo name="res_pjsip_outbound_registration" language="en_US">
43 <synopsis>SIP resource for outbound registrations</synopsis>
45 <emphasis>Outbound Registration</emphasis>
47 <para>This module allows <literal>res_pjsip</literal> to register to other SIP servers.</para>
49 <configFile name="pjsip.conf">
50 <configObject name="registration">
51 <synopsis>The configuration for outbound registration</synopsis>
53 Registration is <emphasis>COMPLETELY</emphasis> separate from the rest of
54 <literal>pjsip.conf</literal>. A minimal configuration consists of
55 setting a <literal>server_uri</literal> and a <literal>client_uri</literal>.
57 <configOption name="auth_rejection_permanent" default="yes">
58 <synopsis>Determines whether failed authentication challenges are treated
59 as permanent failures.</synopsis>
60 <description><para>If this option is enabled and an authentication challenge fails,
61 registration will not be attempted again until the configuration is reloaded.</para></description>
63 <configOption name="client_uri">
64 <synopsis>Client SIP URI used when attemping outbound registration</synopsis>
66 This is the address-of-record for the outbound registration (i.e. the URI in
67 the To header of the REGISTER).</para>
68 <para>For registration with an ITSP, the client SIP URI may need to consist of
69 an account name or number and the provider's hostname for their registrar, e.g.
70 client_uri=1234567890@example.com. This may differ between providers.</para>
71 <para>For registration to generic registrars, the client SIP URI will depend
72 on networking specifics and configuration of the registrar.
75 <configOption name="contact_user">
76 <synopsis>Contact User to use in request</synopsis>
78 <configOption name="expiration" default="3600">
79 <synopsis>Expiration time for registrations in seconds</synopsis>
81 <configOption name="max_retries" default="10">
82 <synopsis>Maximum number of registration attempts.</synopsis>
84 <configOption name="outbound_auth" default="">
85 <synopsis>Authentication object(s) to be used for outbound registrations.</synopsis>
87 This is a comma-delimited list of <replaceable>auth</replaceable>
88 sections defined in <filename>pjsip.conf</filename> used to respond
89 to outbound authentication challenges.</para>
91 Using the same auth section for inbound and outbound
92 authentication is not recommended. There is a difference in
93 meaning for an empty realm setting between inbound and outbound
94 authentication uses. See the auth realm description for details.
98 <configOption name="outbound_proxy" default="">
99 <synopsis>Full SIP URI of the outbound proxy used to send registrations</synopsis>
101 <configOption name="retry_interval" default="60">
102 <synopsis>Interval in seconds between retries if outbound registration is unsuccessful</synopsis>
104 <configOption name="forbidden_retry_interval" default="0">
105 <synopsis>Interval used when receiving a 403 Forbidden response.</synopsis>
107 If a 403 Forbidden is received, chan_pjsip will wait
108 <replaceable>forbidden_retry_interval</replaceable> seconds before
109 attempting registration again. If 0 is specified, chan_pjsip will not
110 retry after receiving a 403 Forbidden response. Setting this to a non-zero
111 value goes against a "SHOULD NOT" in RFC3261, but can be used to work around
113 </para></description>
115 <configOption name="fatal_retry_interval" default="0">
116 <synopsis>Interval used when receiving a Fatal response.</synopsis>
118 If a fatal response is received, chan_pjsip will wait
119 <replaceable>fatal_retry_interval</replaceable> seconds before
120 attempting registration again. If 0 is specified, chan_pjsip will not
121 retry after receiving a fatal (non-temporary 4xx, 5xx, 6xx) response.
122 Setting this to a non-zero value may go against a "SHOULD NOT" in RFC3261,
123 but can be used to work around buggy registrars.</para>
124 <note><para>if also set the <replaceable>forbidden_retry_interval</replaceable>
125 takes precedence over this one when a 403 is received.
126 Also, if <replaceable>auth_rejection_permanent</replaceable> equals 'yes' then
127 a 401 and 407 become subject to this retry interval.</para></note>
130 <configOption name="server_uri">
131 <synopsis>SIP URI of the server to register against</synopsis>
133 This is the URI at which to find the registrar to send the outbound REGISTER. This URI
134 is used as the request URI of the outbound REGISTER request from Asterisk.</para>
135 <para>For registration with an ITSP, the setting may often be just the domain of
136 the registrar, e.g. sip:sip.example.com.
137 </para></description>
139 <configOption name="transport">
140 <synopsis>Transport used for outbound authentication</synopsis>
142 <note><para>A <replaceable>transport</replaceable> configured in
143 <literal>pjsip.conf</literal>. As with other <literal>res_pjsip</literal> modules, this will use the first available transport of the appropriate type if unconfigured.</para></note>
146 <configOption name="line">
147 <synopsis>Whether to add a 'line' parameter to the Contact for inbound call matching</synopsis>
149 When enabled this option will cause a 'line' parameter to be added to the Contact
150 header placed into the outgoing registration request. If the remote server sends a call
151 this line parameter will be used to establish a relationship to the outbound registration,
152 ultimately causing the configured endpoint to be used.
153 </para></description>
155 <configOption name="endpoint">
156 <synopsis>Endpoint to use for incoming related calls</synopsis>
158 When line support is enabled this configured endpoint name is used for incoming calls
159 that are related to the outbound registration.
160 </para></description>
162 <configOption name="type">
163 <synopsis>Must be of type 'registration'.</synopsis>
165 <configOption name="support_path">
166 <synopsis>Enables Path support for outbound REGISTER requests.</synopsis>
168 When this option is enabled, outbound REGISTER requests will advertise
169 support for Path headers so that intervening proxies can add to the Path
171 </para></description>
176 <manager name="PJSIPUnregister" language="en_US">
178 Unregister an outbound registration.
181 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
182 <parameter name="Registration" required="true">
183 <para>The outbound registration to unregister or '*all' to unregister them all.</para>
188 Unregisters the specified (or all) outbound registration(s) and stops future registration attempts.
189 Call PJSIPRegister to start registration and schedule re-registrations according to configuration.
193 <manager name="PJSIPRegister" language="en_US">
195 Register an outbound registration.
198 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
199 <parameter name="Registration" required="true">
200 <para>The outbound registration to register or '*all' to register them all.</para>
205 Unregisters the specified (or all) outbound registration(s) then starts registration and schedules re-registrations
206 according to configuration.
210 <manager name="PJSIPShowRegistrationsOutbound" language="en_US">
212 Lists PJSIP outbound registrations.
217 In response <literal>OutboundRegistrationDetail</literal> events showing configuration and status
218 information are raised for each outbound registration object. <literal>AuthDetail</literal>
219 events are raised for each associated auth object as well. Once all events are completed an
220 <literal>OutboundRegistrationDetailComplete</literal> is issued.
226 /*! \brief Some thread local storage used to determine if the running thread invoked the callback */
227 AST_THREADSTORAGE(register_callback_invoked);
229 /*! \brief Amount of buffer time (in seconds) before expiration that we re-register at */
230 #define REREGISTER_BUFFER_TIME 10
232 /*! \brief Size of the buffer for creating a unique string for the line */
233 #define LINE_PARAMETER_SIZE 8
235 /*! \brief Various states that an outbound registration may be in */
236 enum sip_outbound_registration_status {
237 /*! \brief Currently unregistered */
238 SIP_REGISTRATION_UNREGISTERED = 0,
239 /*! \brief Registered, yay! */
240 SIP_REGISTRATION_REGISTERED,
241 /*! \brief Registration was rejected, but response was temporal */
242 SIP_REGISTRATION_REJECTED_TEMPORARY,
243 /*! \brief Registration was rejected, permanently */
244 SIP_REGISTRATION_REJECTED_PERMANENT,
245 /*! \brief Registration is stopping. */
246 SIP_REGISTRATION_STOPPING,
247 /*! \brief Registration has been stopped */
248 SIP_REGISTRATION_STOPPED,
253 * \brief Convert the internal registration state to an external status string.
256 * \param state Current outbound registration state.
258 * \return External registration status string.
260 static const char *sip_outbound_registration_status_str(enum sip_outbound_registration_status state)
264 str = "Unregistered";
266 case SIP_REGISTRATION_STOPPING:
267 case SIP_REGISTRATION_STOPPED:
268 case SIP_REGISTRATION_UNREGISTERED:
270 case SIP_REGISTRATION_REGISTERED:
273 case SIP_REGISTRATION_REJECTED_TEMPORARY:
274 case SIP_REGISTRATION_REJECTED_PERMANENT:
281 /*! \brief Outbound registration information */
282 struct sip_outbound_registration {
283 /*! \brief Sorcery object details */
284 SORCERY_OBJECT(details);
285 /*! \brief Stringfields */
286 AST_DECLARE_STRING_FIELDS(
287 /*! \brief URI for the registrar */
288 AST_STRING_FIELD(server_uri);
289 /*! \brief URI for the AOR */
290 AST_STRING_FIELD(client_uri);
291 /*! \brief Optional user for contact header */
292 AST_STRING_FIELD(contact_user);
293 /*! \brief Explicit transport to use for registration */
294 AST_STRING_FIELD(transport);
295 /*! \brief Outbound proxy to use */
296 AST_STRING_FIELD(outbound_proxy);
297 /*! \brief Endpoint to use for related incoming calls */
298 AST_STRING_FIELD(endpoint);
300 /*! \brief Requested expiration time */
301 unsigned int expiration;
302 /*! \brief Interval at which retries should occur for temporal responses */
303 unsigned int retry_interval;
304 /*! \brief Interval at which retries should occur for permanent responses */
305 unsigned int forbidden_retry_interval;
306 /*! \brief Interval at which retries should occur for all permanent responses */
307 unsigned int fatal_retry_interval;
308 /*! \brief Treat authentication challenges that we cannot handle as permanent failures */
309 unsigned int auth_rejection_permanent;
310 /*! \brief Maximum number of retries permitted */
311 unsigned int max_retries;
312 /*! \brief Whether to add a line parameter to the outbound Contact or not */
314 /*! \brief Configured authentication credentials */
315 struct ast_sip_auth_vector outbound_auths;
316 /*! \brief Whether Path support is enabled */
317 unsigned int support_path;
320 /*! \brief Outbound registration client state information (persists for lifetime of regc) */
321 struct sip_outbound_registration_client_state {
322 /*! \brief Current state of this registration */
323 enum sip_outbound_registration_status status;
325 * \brief Outbound registration client
326 * \note May only be accessed within the serializer thread
327 * because it might get destroyed and set to NULL for
331 /*! \brief Timer entry for retrying on temporal responses */
332 pj_timer_entry timer;
333 /*! \brief Optional line parameter placed into Contact */
334 char line[LINE_PARAMETER_SIZE];
335 /*! \brief Current number of retries */
336 unsigned int retries;
337 /*! \brief Maximum number of retries permitted */
338 unsigned int max_retries;
339 /*! \brief Interval at which retries should occur for temporal responses */
340 unsigned int retry_interval;
341 /*! \brief Interval at which retries should occur for permanent responses */
342 unsigned int forbidden_retry_interval;
343 /*! \brief Interval at which retries should occur for all permanent responses */
344 unsigned int fatal_retry_interval;
345 /*! \brief Treat authentication challenges that we cannot handle as permanent failures */
346 unsigned int auth_rejection_permanent;
347 /*! \brief Determines whether SIP Path support should be advertised */
348 unsigned int support_path;
349 /*! CSeq number of last sent auth request. */
350 unsigned int auth_cseq;
351 /*! \brief Serializer for stuff and things */
352 struct ast_taskprocessor *serializer;
353 /*! \brief Configured authentication credentials */
354 struct ast_sip_auth_vector outbound_auths;
355 /*! \brief Registration should be destroyed after completion of transaction */
356 unsigned int destroy:1;
357 /*! \brief Non-zero if we have attempted sending a REGISTER with authentication */
358 unsigned int auth_attempted:1;
359 /*! \brief The name of the transport to be used for the registration */
360 char *transport_name;
363 /*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
364 struct sip_outbound_registration_state {
365 /*! \brief Outbound registration configuration object */
366 struct sip_outbound_registration *registration;
367 /*! \brief Client state information */
368 struct sip_outbound_registration_client_state *client_state;
371 /*! Time needs to be long enough for a transaction to timeout if nothing replies. */
372 #define MAX_UNLOAD_TIMEOUT_TIME 35 /* Seconds */
374 /*! Shutdown group to monitor sip_outbound_registration_client_state serializers. */
375 static struct ast_serializer_shutdown_group *shutdown_group;
377 /*! \brief Default number of state container buckets */
378 #define DEFAULT_STATE_BUCKETS 53
379 static AO2_GLOBAL_OBJ_STATIC(current_states);
381 /*! subscription id for network change events */
382 static struct stasis_subscription *network_change_sub;
384 /*! \brief hashing function for state objects */
385 static int registration_state_hash(const void *obj, const int flags)
387 const struct sip_outbound_registration_state *object;
390 switch (flags & OBJ_SEARCH_MASK) {
394 case OBJ_SEARCH_OBJECT:
396 key = ast_sorcery_object_get_id(object->registration);
402 return ast_str_hash(key);
405 /*! \brief comparator function for state objects */
406 static int registration_state_cmp(void *obj, void *arg, int flags)
408 const struct sip_outbound_registration_state *object_left = obj;
409 const struct sip_outbound_registration_state *object_right = arg;
410 const char *right_key = arg;
413 switch (flags & OBJ_SEARCH_MASK) {
414 case OBJ_SEARCH_OBJECT:
415 right_key = ast_sorcery_object_get_id(object_right->registration);
418 cmp = strcmp(ast_sorcery_object_get_id(object_left->registration), right_key);
420 case OBJ_SEARCH_PARTIAL_KEY:
421 /* Not supported by container. */
434 static struct sip_outbound_registration_state *get_state(const char *id)
436 struct sip_outbound_registration_state *state = NULL;
437 struct ao2_container *states;
439 states = ao2_global_obj_ref(current_states);
441 state = ao2_find(states, id, OBJ_SEARCH_KEY);
447 static struct ao2_container *get_registrations(void)
449 struct ao2_container *registrations = ast_sorcery_retrieve_by_fields(
450 ast_sip_get_sorcery(), "registration",
451 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
453 return registrations;
456 /*! \brief Callback function for matching an outbound registration based on line */
457 static int line_identify_relationship(void *obj, void *arg, int flags)
459 struct sip_outbound_registration_state *state = obj;
460 pjsip_param *line = arg;
462 return !pj_strcmp2(&line->value, state->client_state->line) ? CMP_MATCH | CMP_STOP : 0;
465 static struct pjsip_param *get_uri_option_line(const void *uri)
467 pjsip_sip_uri *pjuri;
468 static const pj_str_t LINE_STR = { "line", 4 };
470 if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) {
473 pjuri = pjsip_uri_get_uri(uri);
474 return pjsip_param_find(&pjuri->other_param, &LINE_STR);
477 /*! \brief Endpoint identifier which uses the 'line' parameter to establish a relationship to an outgoing registration */
478 static struct ast_sip_endpoint *line_identify(pjsip_rx_data *rdata)
481 RAII_VAR(struct ao2_container *, states, NULL, ao2_cleanup);
482 RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
484 if (!(line = get_uri_option_line(rdata->msg_info.to->uri))
485 && !(line = get_uri_option_line(rdata->msg_info.msg->line.req.uri))) {
489 states = ao2_global_obj_ref(current_states);
494 state = ao2_callback(states, 0, line_identify_relationship, line);
495 if (!state || ast_strlen_zero(state->registration->endpoint)) {
499 ast_debug(3, "Determined relationship to outbound registration '%s' based on line '%s', using configured endpoint '%s'\n",
500 ast_sorcery_object_get_id(state->registration), state->client_state->line, state->registration->endpoint);
502 return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", state->registration->endpoint);
505 static struct ast_sip_endpoint_identifier line_identifier = {
506 .identify_endpoint = line_identify,
509 /*! \brief Helper function which cancels the timer on a client */
510 static void cancel_registration(struct sip_outbound_registration_client_state *client_state)
512 if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &client_state->timer)) {
513 /* The timer was successfully cancelled, drop the refcount of client_state */
514 ao2_ref(client_state, -1);
518 static pj_str_t PATH_NAME = { "path", 4 };
520 /*! \brief Helper function which sends a message and cleans up, if needed, on failure */
521 static pj_status_t registration_client_send(struct sip_outbound_registration_client_state *client_state,
522 pjsip_tx_data *tdata)
525 int *callback_invoked;
526 pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
528 callback_invoked = ast_threadstorage_get(®ister_callback_invoked, sizeof(int));
529 if (!callback_invoked) {
530 pjsip_tx_data_dec_ref(tdata);
533 *callback_invoked = 0;
535 /* Due to the message going out the callback may now be invoked, so bump the count */
536 ao2_ref(client_state, +1);
538 * Set the transport in case transports were reloaded.
539 * When pjproject removes the extraneous error messages produced,
540 * we can check status and only set the transport and resend if there was an error
542 ast_sip_set_tpselector_from_transport_name(client_state->transport_name, &selector);
543 pjsip_regc_set_transport(client_state->client, &selector);
544 status = pjsip_regc_send(client_state->client, tdata);
546 /* If the attempt to send the message failed and the callback was not invoked we need to
547 * drop the reference we just added
549 if ((status != PJ_SUCCESS) && !(*callback_invoked)) {
550 ao2_ref(client_state, -1);
556 /*! \brief Callback function for registering */
557 static int handle_client_registration(void *data)
559 RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
560 pjsip_tx_data *tdata;
561 pjsip_regc_info info;
562 char server_uri[PJSIP_MAX_URL_SIZE];
563 char client_uri[PJSIP_MAX_URL_SIZE];
565 if (client_state->status == SIP_REGISTRATION_STOPPED
566 || pjsip_regc_register(client_state->client, PJ_FALSE, &tdata) != PJ_SUCCESS) {
570 pjsip_regc_get_info(client_state->client, &info);
571 ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
572 ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
573 ast_debug(1, "Outbound REGISTER attempt %u to '%s' with client '%s'\n",
574 client_state->retries + 1, server_uri, client_uri);
576 if (client_state->support_path) {
577 pjsip_supported_hdr *hdr;
579 hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL);
581 /* insert a new Supported header */
582 hdr = pjsip_supported_hdr_create(tdata->pool);
584 pjsip_tx_data_dec_ref(tdata);
588 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
591 /* add on to the existing Supported header */
592 pj_strassign(&hdr->values[hdr->count++], &PATH_NAME);
595 registration_client_send(client_state, tdata);
600 /*! \brief Timer callback function, used just for registrations */
601 static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
603 struct sip_outbound_registration_client_state *client_state = entry->user_data;
608 * Transfer client_state reference to serializer task so the
609 * nominal path will not dec the client_state ref in this
610 * pjproject callback thread.
612 if (ast_sip_push_task(client_state->serializer, handle_client_registration, client_state)) {
613 ast_log(LOG_WARNING, "Scheduled outbound registration could not be executed.\n");
614 ao2_ref(client_state, -1);
618 /*! \brief Helper function which sets up the timer to re-register in a specific amount of time */
619 static void schedule_registration(struct sip_outbound_registration_client_state *client_state, unsigned int seconds)
621 pj_time_val delay = { .sec = seconds, };
622 pjsip_regc_info info;
624 cancel_registration(client_state);
626 pjsip_regc_get_info(client_state->client, &info);
627 ast_debug(1, "Scheduling outbound registration to server '%.*s' from client '%.*s' in %d seconds\n",
628 (int) info.server_uri.slen, info.server_uri.ptr,
629 (int) info.client_uri.slen, info.client_uri.ptr,
632 ao2_ref(client_state, +1);
633 if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &client_state->timer, &delay) != PJ_SUCCESS) {
634 ast_log(LOG_WARNING, "Failed to schedule registration to server '%.*s' from client '%.*s'\n",
635 (int) info.server_uri.slen, info.server_uri.ptr,
636 (int) info.client_uri.slen, info.client_uri.ptr);
637 ao2_ref(client_state, -1);
641 static void update_client_state_status(struct sip_outbound_registration_client_state *client_state, enum sip_outbound_registration_status status)
643 const char *status_old;
644 const char *status_new;
646 if (client_state->status == status) {
647 /* Status state did not change at all. */
651 status_old = sip_outbound_registration_status_str(client_state->status);
652 status_new = sip_outbound_registration_status_str(status);
653 client_state->status = status;
655 if (!strcmp(status_old, status_new)) {
657 * The internal status state may have changed but the status
658 * state we tell the world did not change at all.
663 ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "-1", 1.0,
665 ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0,
669 /*! \brief Callback function for unregistering (potentially) and destroying state */
670 static int handle_client_state_destruction(void *data)
672 struct sip_outbound_registration_client_state *client_state = data;
674 cancel_registration(client_state);
676 if (client_state->client) {
677 pjsip_regc_info info;
678 pjsip_tx_data *tdata;
680 pjsip_regc_get_info(client_state->client, &info);
682 if (info.is_busy == PJ_TRUE) {
683 /* If a client transaction is in progress we defer until it is complete */
685 "Registration transaction is busy with server '%.*s' from client '%.*s'.\n",
686 (int) info.server_uri.slen, info.server_uri.ptr,
687 (int) info.client_uri.slen, info.client_uri.ptr);
688 client_state->destroy = 1;
689 ao2_ref(client_state, -1);
693 switch (client_state->status) {
694 case SIP_REGISTRATION_UNREGISTERED:
696 case SIP_REGISTRATION_REGISTERED:
698 "Trying to unregister with server '%.*s' from client '%.*s' before destruction.\n",
699 (int) info.server_uri.slen, info.server_uri.ptr,
700 (int) info.client_uri.slen, info.client_uri.ptr);
702 update_client_state_status(client_state, SIP_REGISTRATION_STOPPING);
703 client_state->destroy = 1;
704 if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS
705 && registration_client_send(client_state, tdata) == PJ_SUCCESS) {
706 ao2_ref(client_state, -1);
710 case SIP_REGISTRATION_REJECTED_TEMPORARY:
711 case SIP_REGISTRATION_REJECTED_PERMANENT:
712 case SIP_REGISTRATION_STOPPING:
713 case SIP_REGISTRATION_STOPPED:
717 pjsip_regc_destroy(client_state->client);
718 client_state->client = NULL;
721 update_client_state_status(client_state, SIP_REGISTRATION_STOPPED);
722 ast_sip_auth_vector_destroy(&client_state->outbound_auths);
723 ao2_ref(client_state, -1);
728 /*! \brief Structure for registration response */
729 struct registration_response {
730 /*! \brief Response code for the registration attempt */
732 /*! \brief Expiration time for registration */
734 /*! \brief Retry-After value */
736 /*! \brief Outbound registration client state */
737 struct sip_outbound_registration_client_state *client_state;
738 /*! \brief The response message */
739 pjsip_rx_data *rdata;
740 /*! \brief Request for which the response was received */
741 pjsip_tx_data *old_request;
744 /*! \brief Registration response structure destructor */
745 static void registration_response_destroy(void *obj)
747 struct registration_response *response = obj;
749 if (response->rdata) {
750 pjsip_rx_data_free_cloned(response->rdata);
753 if (response->old_request) {
754 pjsip_tx_data_dec_ref(response->old_request);
757 ao2_cleanup(response->client_state);
760 /*! \brief Helper function which determines if a response code is temporal or not */
761 static int sip_outbound_registration_is_temporal(unsigned int code,
762 struct sip_outbound_registration_client_state *client_state)
764 /* Shamelessly taken from pjsua */
765 if (code == PJSIP_SC_REQUEST_TIMEOUT ||
766 code == PJSIP_SC_INTERNAL_SERVER_ERROR ||
767 code == PJSIP_SC_BAD_GATEWAY ||
768 code == PJSIP_SC_SERVICE_UNAVAILABLE ||
769 code == PJSIP_SC_SERVER_TIMEOUT ||
770 ((code == PJSIP_SC_UNAUTHORIZED ||
771 code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED) &&
772 !client_state->auth_rejection_permanent) ||
773 PJSIP_IS_STATUS_IN_CLASS(code, 600)) {
780 static void schedule_retry(struct registration_response *response, unsigned int interval,
781 const char *server_uri, const char *client_uri)
783 update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);
784 schedule_registration(response->client_state, interval);
786 if (response->rdata) {
787 ast_log(LOG_WARNING, "Temporal response '%d' received from '%s' on "
788 "registration attempt to '%s', retrying in '%u'\n",
789 response->code, server_uri, client_uri, interval);
791 ast_log(LOG_WARNING, "No response received from '%s' on "
792 "registration attempt to '%s', retrying in '%u'\n",
793 server_uri, client_uri, interval);
797 /*! \brief Callback function for handling a response to a registration attempt */
798 static int handle_registration_response(void *data)
800 struct registration_response *response = data;
801 pjsip_regc_info info;
802 char server_uri[PJSIP_MAX_URL_SIZE];
803 char client_uri[PJSIP_MAX_URL_SIZE];
805 if (response->client_state->status == SIP_REGISTRATION_STOPPED) {
806 ao2_ref(response, -1);
810 pjsip_regc_get_info(response->client_state->client, &info);
811 ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
812 ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
814 ast_debug(1, "Processing REGISTER response %d from server '%s' for client '%s'\n",
815 response->code, server_uri, client_uri);
817 if ((response->code == 401 || response->code == 407)
818 && (!response->client_state->auth_attempted
819 || response->rdata->msg_info.cseq->cseq != response->client_state->auth_cseq)) {
821 pjsip_cseq_hdr *cseq_hdr;
822 pjsip_tx_data *tdata;
824 if (!ast_sip_create_request_with_auth(&response->client_state->outbound_auths,
825 response->rdata, response->old_request, &tdata)) {
826 response->client_state->auth_attempted = 1;
827 ast_debug(1, "Sending authenticated REGISTER to server '%s' from client '%s'\n",
828 server_uri, client_uri);
829 pjsip_tx_data_add_ref(tdata);
830 res = registration_client_send(response->client_state, tdata);
832 /* Save the cseq that actually got sent. */
833 cseq_hdr = (pjsip_cseq_hdr *) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ,
835 response->client_state->auth_cseq = cseq_hdr->cseq;
836 pjsip_tx_data_dec_ref(tdata);
837 if (res == PJ_SUCCESS) {
838 ao2_ref(response, -1);
842 ast_log(LOG_WARNING, "Failed to create authenticated REGISTER request to server '%s' from client '%s'\n",
843 server_uri, client_uri);
845 /* Otherwise, fall through so the failure is processed appropriately */
848 response->client_state->auth_attempted = 0;
850 if (PJSIP_IS_STATUS_IN_CLASS(response->code, 200)) {
851 /* Check if this is in regards to registering or unregistering */
852 if (response->expiration) {
853 int next_registration_round;
855 /* If the registration went fine simply reschedule registration for the future */
856 ast_debug(1, "Outbound registration to '%s' with client '%s' successful\n", server_uri, client_uri);
857 update_client_state_status(response->client_state, SIP_REGISTRATION_REGISTERED);
858 response->client_state->retries = 0;
859 next_registration_round = response->expiration - REREGISTER_BUFFER_TIME;
860 if (next_registration_round < 0) {
861 /* Re-register immediately. */
862 next_registration_round = 0;
864 schedule_registration(response->client_state, next_registration_round);
866 ast_debug(1, "Outbound unregistration to '%s' with client '%s' successful\n", server_uri, client_uri);
867 update_client_state_status(response->client_state, SIP_REGISTRATION_UNREGISTERED);
869 } else if (response->client_state->destroy) {
870 /* We need to deal with the pending destruction instead. */
871 } else if (response->retry_after) {
872 /* If we have been instructed to retry after a period of time, schedule it as such */
873 schedule_retry(response, response->retry_after, server_uri, client_uri);
874 } else if (response->client_state->retry_interval
875 && sip_outbound_registration_is_temporal(response->code, response->client_state)) {
876 if (response->client_state->retries == response->client_state->max_retries) {
877 /* If we received enough temporal responses to exceed our maximum give up permanently */
878 update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT);
879 ast_log(LOG_WARNING, "Maximum retries reached when attempting outbound registration to '%s' with client '%s', stopping registration attempt\n",
880 server_uri, client_uri);
882 /* On the other hand if we can still try some more do so */
883 response->client_state->retries++;
884 schedule_retry(response, response->client_state->retry_interval, server_uri, client_uri);
887 if (response->code == 403
888 && response->client_state->forbidden_retry_interval
889 && response->client_state->retries < response->client_state->max_retries) {
890 /* A forbidden response retry interval is configured and there are retries remaining */
891 update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);
892 response->client_state->retries++;
893 schedule_registration(response->client_state, response->client_state->forbidden_retry_interval);
894 ast_log(LOG_WARNING, "403 Forbidden fatal response received from '%s' on registration attempt to '%s', retrying in '%u' seconds\n",
895 server_uri, client_uri, response->client_state->forbidden_retry_interval);
896 } else if (response->client_state->fatal_retry_interval
897 && response->client_state->retries < response->client_state->max_retries) {
898 /* Some kind of fatal failure response received, so retry according to configured interval */
899 update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);
900 response->client_state->retries++;
901 schedule_registration(response->client_state, response->client_state->fatal_retry_interval);
902 ast_log(LOG_WARNING, "'%d' fatal response received from '%s' on registration attempt to '%s', retrying in '%u' seconds\n",
903 response->code, server_uri, client_uri, response->client_state->fatal_retry_interval);
905 /* Finally if there's no hope of registering give up */
906 update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT);
907 if (response->rdata) {
908 ast_log(LOG_WARNING, "Fatal response '%d' received from '%s' on registration attempt to '%s', stopping outbound registration\n",
909 response->code, server_uri, client_uri);
911 ast_log(LOG_WARNING, "Fatal registration attempt to '%s', stopping outbound registration\n", client_uri);
916 ast_system_publish_registry("PJSIP", client_uri, server_uri,
917 sip_outbound_registration_status_str(response->client_state->status), NULL);
919 if (response->client_state->destroy) {
920 /* We have a pending deferred destruction to complete now. */
921 ao2_ref(response->client_state, +1);
922 handle_client_state_destruction(response->client_state);
925 ao2_ref(response, -1);
929 /*! \brief Callback function for outbound registration client */
930 static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *param)
932 struct sip_outbound_registration_client_state *client_state = param->token;
933 struct registration_response *response;
934 int *callback_invoked;
936 callback_invoked = ast_threadstorage_get(®ister_callback_invoked, sizeof(int));
938 ast_assert(callback_invoked != NULL);
939 ast_assert(client_state != NULL);
941 *callback_invoked = 1;
943 response = ao2_alloc(sizeof(*response), registration_response_destroy);
945 ao2_ref(client_state, -1);
948 response->code = param->code;
949 response->expiration = param->expiration;
951 * Transfer client_state reference to response so the
952 * nominal path will not dec the client_state ref in this
953 * pjproject callback thread.
955 response->client_state = client_state;
957 ast_debug(1, "Received REGISTER response %d(%.*s)\n",
958 param->code, (int) param->reason.slen, param->reason.ptr);
961 struct pjsip_retry_after_hdr *retry_after;
962 pjsip_transaction *tsx;
964 retry_after = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_RETRY_AFTER,
966 response->retry_after = retry_after ? retry_after->ivalue : 0;
967 tsx = pjsip_rdata_get_tsx(param->rdata);
968 response->old_request = tsx->last_tx;
969 pjsip_tx_data_add_ref(response->old_request);
970 pjsip_rx_data_clone(param->rdata, 0, &response->rdata);
974 * Transfer response reference to serializer task so the
975 * nominal path will not dec the response ref in this
976 * pjproject callback thread.
978 if (ast_sip_push_task(client_state->serializer, handle_registration_response, response)) {
979 ast_log(LOG_WARNING, "Failed to pass incoming registration response to threadpool\n");
980 ao2_cleanup(response);
984 /*! \brief Destructor function for registration state */
985 static void sip_outbound_registration_state_destroy(void *obj)
987 struct sip_outbound_registration_state *state = obj;
989 ast_debug(3, "Destroying registration state for registration to server '%s' from client '%s'\n",
990 state->registration->server_uri, state->registration->client_uri);
991 ao2_cleanup(state->registration);
993 if (!state->client_state) {
995 } else if (!state->client_state->serializer) {
996 ao2_ref(state->client_state, -1);
997 } else if (ast_sip_push_task(state->client_state->serializer,
998 handle_client_state_destruction, state->client_state)) {
999 ast_log(LOG_WARNING, "Failed to pass outbound registration client destruction to threadpool\n");
1000 ao2_ref(state->client_state, -1);
1004 /*! \brief Destructor function for client registration state */
1005 static void sip_outbound_registration_client_state_destroy(void *obj)
1007 struct sip_outbound_registration_client_state *client_state = obj;
1009 ast_free(client_state->transport_name);
1010 ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "-1", 1.0);
1011 ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "-1", 1.0,
1012 sip_outbound_registration_status_str(client_state->status));
1014 ast_taskprocessor_unreference(client_state->serializer);
1017 /*! \brief Allocator function for registration state */
1018 static struct sip_outbound_registration_state *sip_outbound_registration_state_alloc(struct sip_outbound_registration *registration)
1020 struct sip_outbound_registration_state *state;
1021 char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
1023 state = ao2_alloc(sizeof(*state), sip_outbound_registration_state_destroy);
1027 state->client_state = ao2_alloc(sizeof(*state->client_state),
1028 sip_outbound_registration_client_state_destroy);
1029 if (!state->client_state) {
1034 /* Create name with seq number appended. */
1035 ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/outreg/%s",
1036 ast_sorcery_object_get_id(registration));
1038 state->client_state->serializer = ast_sip_create_serializer_group(tps_name,
1040 if (!state->client_state->serializer) {
1044 state->client_state->status = SIP_REGISTRATION_UNREGISTERED;
1045 state->client_state->timer.user_data = state->client_state;
1046 state->client_state->timer.cb = sip_outbound_registration_timer_cb;
1047 state->client_state->transport_name = ast_strdup(registration->transport);
1049 ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "+1", 1.0);
1050 ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0,
1051 sip_outbound_registration_status_str(state->client_state->status));
1053 state->registration = ao2_bump(registration);
1057 /*! \brief Destructor function for registration information */
1058 static void sip_outbound_registration_destroy(void *obj)
1060 struct sip_outbound_registration *registration = obj;
1062 ast_sip_auth_vector_destroy(®istration->outbound_auths);
1064 ast_string_field_free_memory(registration);
1067 /*! \brief Allocator function for registration information */
1068 static void *sip_outbound_registration_alloc(const char *name)
1070 struct sip_outbound_registration *registration;
1072 registration = ast_sorcery_generic_alloc(sizeof(*registration),
1073 sip_outbound_registration_destroy);
1074 if (!registration || ast_string_field_init(registration, 256)) {
1075 ao2_cleanup(registration);
1079 return registration;
1082 /*! \brief Helper function which populates a pj_str_t with a contact header */
1083 static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const char *user,
1084 const pj_str_t *target, pjsip_tpselector *selector, const char *line)
1086 pj_str_t tmp, local_addr;
1088 pjsip_sip_uri *sip_uri;
1089 pjsip_transport_type_e type;
1092 pj_strdup_with_null(pool, &tmp, target);
1094 if (!(uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0)) ||
1095 (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))) {
1099 sip_uri = pjsip_uri_get_uri(uri);
1101 type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
1102 if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri)) {
1103 if (type == PJSIP_TRANSPORT_UNSPECIFIED
1104 || !(pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE)) {
1105 type = PJSIP_TRANSPORT_TLS;
1107 } else if (!sip_uri->transport_param.slen) {
1108 type = PJSIP_TRANSPORT_UDP;
1109 } else if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
1113 if (pj_strchr(&sip_uri->host, ':')) {
1114 type |= PJSIP_TRANSPORT_IPV6;
1117 if (pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()),
1118 pool, type, selector, &local_addr, &local_port) != PJ_SUCCESS) {
1122 if (!pj_strchr(&sip_uri->host, ':') && pj_strchr(&local_addr, ':')) {
1123 type |= PJSIP_TRANSPORT_IPV6;
1126 contact->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
1127 contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
1128 "<%s:%s@%s%.*s%s:%d%s%s%s%s>",
1129 ((pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE) && PJSIP_URI_SCHEME_IS_SIPS(uri)) ? "sips" : "sip",
1131 (type & PJSIP_TRANSPORT_IPV6) ? "[" : "",
1132 (int)local_addr.slen,
1134 (type & PJSIP_TRANSPORT_IPV6) ? "]" : "",
1136 (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? ";transport=" : "",
1137 (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? pjsip_transport_get_type_name(type) : "",
1138 !ast_strlen_zero(line) ? ";line=" : "",
1146 * \brief Check if a registration can be reused
1148 * This checks if the existing outbound registration's configuration differs from a newly-applied
1149 * outbound registration to see if the applied one.
1151 * \param existing The pre-existing outbound registration
1152 * \param applied The newly-created registration
1154 static int can_reuse_registration(struct sip_outbound_registration *existing,
1155 struct sip_outbound_registration *applied)
1158 struct ast_sorcery *sorcery = ast_sip_get_sorcery();
1159 struct ast_variable *ve = ast_sorcery_objectset_create(sorcery, existing);
1160 struct ast_variable *va = ast_sorcery_objectset_create(sorcery, applied);
1161 struct ast_variable *vc = NULL;
1163 if (ast_sorcery_changeset_create(ve, va, &vc) || vc != NULL) {
1165 ast_debug(4, "Registration '%s' changed. Can't re-use.\n", ast_sorcery_object_get_id(existing));
1167 ast_debug(4, "Registration '%s' didn't change. Can re-use\n", ast_sorcery_object_get_id(existing));
1170 ast_variables_destroy(ve);
1171 ast_variables_destroy(va);
1172 ast_variables_destroy(vc);
1177 /*! \brief Helper function that allocates a pjsip registration client and configures it */
1178 static int sip_outbound_registration_regc_alloc(void *data)
1180 struct sip_outbound_registration_state *state = data;
1181 RAII_VAR(struct sip_outbound_registration *, registration,
1182 ao2_bump(state->registration), ao2_cleanup);
1186 pj_str_t server_uri, client_uri, contact_uri;
1187 pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
1189 pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "URI Validation", 256, 256);
1191 ast_log(LOG_ERROR, "Could not create pool for URI validation on outbound registration '%s'\n",
1192 ast_sorcery_object_get_id(registration));
1196 pj_strdup2_with_null(pool, &tmp, registration->server_uri);
1197 uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
1199 ast_log(LOG_ERROR, "Invalid server URI '%s' specified on outbound registration '%s'\n",
1200 registration->server_uri, ast_sorcery_object_get_id(registration));
1201 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
1205 pj_strdup2_with_null(pool, &tmp, registration->client_uri);
1206 uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
1208 ast_log(LOG_ERROR, "Invalid client URI '%s' specified on outbound registration '%s'\n",
1209 registration->client_uri, ast_sorcery_object_get_id(registration));
1210 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
1214 if (!ast_strlen_zero(registration->outbound_proxy)) {
1215 pj_strdup2_with_null(pool, &tmp, registration->outbound_proxy);
1216 uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
1218 ast_log(LOG_ERROR, "Invalid outbound proxy URI '%s' specified on outbound registration '%s'\n",
1219 registration->outbound_proxy, ast_sorcery_object_get_id(registration));
1220 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
1225 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
1228 ast_assert(state->client_state->client == NULL);
1229 if (pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state->client_state,
1230 sip_outbound_registration_response_cb,
1231 &state->client_state->client) != PJ_SUCCESS) {
1235 ast_sip_set_tpselector_from_transport_name(registration->transport, &selector);
1236 pjsip_regc_set_transport(state->client_state->client, &selector);
1238 if (!ast_strlen_zero(registration->outbound_proxy)) {
1239 pjsip_route_hdr route_set, *route;
1240 static const pj_str_t ROUTE_HNAME = { "Route", 5 };
1243 pj_list_init(&route_set);
1245 pj_strdup2_with_null(pjsip_regc_get_pool(state->client_state->client), &tmp,
1246 registration->outbound_proxy);
1247 route = pjsip_parse_hdr(pjsip_regc_get_pool(state->client_state->client),
1248 &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL);
1252 pj_list_insert_nodes_before(&route_set, route);
1254 pjsip_regc_set_route_set(state->client_state->client, &route_set);
1257 if (state->registration->line) {
1258 ast_generate_random_string(state->client_state->line, sizeof(state->client_state->line));
1261 pj_cstr(&server_uri, registration->server_uri);
1264 if (sip_dialog_create_contact(pjsip_regc_get_pool(state->client_state->client),
1265 &contact_uri, S_OR(registration->contact_user, "s"), &server_uri, &selector,
1266 state->client_state->line)) {
1270 pj_cstr(&client_uri, registration->client_uri);
1271 if (pjsip_regc_init(state->client_state->client, &server_uri, &client_uri,
1272 &client_uri, 1, &contact_uri, registration->expiration) != PJ_SUCCESS) {
1279 /*! \brief Helper function which performs a single registration */
1280 static int sip_outbound_registration_perform(void *data)
1282 struct sip_outbound_registration_state *state = data;
1283 struct sip_outbound_registration *registration = ao2_bump(state->registration);
1286 /* Just in case the client state is being reused for this registration, free the auth information */
1287 ast_sip_auth_vector_destroy(&state->client_state->outbound_auths);
1289 AST_VECTOR_INIT(&state->client_state->outbound_auths, AST_VECTOR_SIZE(®istration->outbound_auths));
1290 for (i = 0; i < AST_VECTOR_SIZE(®istration->outbound_auths); ++i) {
1291 const char *name = ast_strdup(AST_VECTOR_GET(®istration->outbound_auths, i));
1294 AST_VECTOR_APPEND(&state->client_state->outbound_auths, name);
1297 state->client_state->retry_interval = registration->retry_interval;
1298 state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval;
1299 state->client_state->fatal_retry_interval = registration->fatal_retry_interval;
1300 state->client_state->max_retries = registration->max_retries;
1301 state->client_state->retries = 0;
1302 state->client_state->support_path = registration->support_path;
1303 state->client_state->auth_rejection_permanent = registration->auth_rejection_permanent;
1305 pjsip_regc_update_expires(state->client_state->client, registration->expiration);
1307 schedule_registration(state->client_state, (ast_random() % 10) + 1);
1309 ao2_ref(registration, -1);
1314 /*! \brief Apply function which finds or allocates a state structure */
1315 static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, void *obj)
1317 RAII_VAR(struct ao2_container *, states, ao2_global_obj_ref(current_states), ao2_cleanup);
1318 RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
1319 RAII_VAR(struct sip_outbound_registration_state *, new_state, NULL, ao2_cleanup);
1320 struct sip_outbound_registration *applied = obj;
1323 /* Global container has gone. Likely shutting down. */
1326 state = ao2_find(states, ast_sorcery_object_get_id(applied), OBJ_SEARCH_KEY);
1328 ast_debug(4, "Applying configuration to outbound registration '%s'\n", ast_sorcery_object_get_id(applied));
1330 if (ast_strlen_zero(applied->server_uri)) {
1331 ast_log(LOG_ERROR, "No server URI specified on outbound registration '%s'\n",
1332 ast_sorcery_object_get_id(applied));
1334 } else if (ast_sip_validate_uri_length(applied->server_uri)) {
1335 ast_log(LOG_ERROR, "Server URI or hostname length exceeds pjpropject limit '%s'\n",
1336 ast_sorcery_object_get_id(applied));
1338 } else if (ast_strlen_zero(applied->client_uri)) {
1339 ast_log(LOG_ERROR, "No client URI specified on outbound registration '%s'\n",
1340 ast_sorcery_object_get_id(applied));
1342 } else if (ast_sip_validate_uri_length(applied->client_uri)) {
1343 ast_log(LOG_ERROR, "Client URI or hostname length exceeds pjpropject limit '%s'\n",
1344 ast_sorcery_object_get_id(applied));
1346 } else if (applied->line && ast_strlen_zero(applied->endpoint)) {
1347 ast_log(LOG_ERROR, "Line support has been enabled on outbound registration '%s' without providing an endpoint\n",
1348 ast_sorcery_object_get_id(applied));
1350 } else if (!ast_strlen_zero(applied->endpoint) && !applied->line) {
1351 ast_log(LOG_ERROR, "An endpoint has been specified on outbound registration '%s' without enabling line support\n",
1352 ast_sorcery_object_get_id(applied));
1356 if (state && can_reuse_registration(state->registration, applied)) {
1358 "No change between old configuration and new configuration on outbound registration '%s'. Using previous state\n",
1359 ast_sorcery_object_get_id(applied));
1362 * This is OK to replace without relinking the state in the
1363 * current_states container since state->registration and
1364 * applied have the same key.
1367 ao2_replace(state->registration, applied);
1372 if (!(new_state = sip_outbound_registration_state_alloc(applied))) {
1376 if (ast_sip_push_task_synchronous(new_state->client_state->serializer,
1377 sip_outbound_registration_regc_alloc, new_state)) {
1381 if (ast_sip_push_task(new_state->client_state->serializer,
1382 sip_outbound_registration_perform, ao2_bump(new_state))) {
1383 ast_log(LOG_ERROR, "Failed to perform outbound registration on '%s'\n",
1384 ast_sorcery_object_get_id(new_state->registration));
1385 ao2_ref(new_state, -1);
1391 ao2_unlink(states, state);
1393 ao2_link(states, new_state);
1399 static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1401 struct sip_outbound_registration *registration = obj;
1403 return ast_sip_auth_vector_init(®istration->outbound_auths, var->value);
1406 static int outbound_auths_to_str(const void *obj, const intptr_t *args, char **buf)
1408 const struct sip_outbound_registration *registration = obj;
1410 return ast_sip_auths_to_str(®istration->outbound_auths, buf);
1413 static int outbound_auths_to_var_list(const void *obj, struct ast_variable **fields)
1415 const struct sip_outbound_registration *registration = obj;
1417 struct ast_variable *head = NULL;
1419 for (i = 0; i < AST_VECTOR_SIZE(®istration->outbound_auths) ; i++) {
1420 ast_variable_list_append(&head, ast_variable_new("outbound_auth",
1421 AST_VECTOR_GET(®istration->outbound_auths, i), ""));
1431 static int unregister_task(void *obj)
1433 struct sip_outbound_registration_state *state = obj;
1434 struct pjsip_regc *client = state->client_state->client;
1435 pjsip_tx_data *tdata;
1436 pjsip_regc_info info;
1438 pjsip_regc_get_info(client, &info);
1439 ast_debug(1, "Unregistering contacts with server '%s' from client '%s'\n",
1440 state->registration->server_uri, state->registration->client_uri);
1442 cancel_registration(state->client_state);
1444 if (pjsip_regc_unregister(client, &tdata) == PJ_SUCCESS) {
1445 registration_client_send(state->client_state, tdata);
1452 static int queue_unregister(struct sip_outbound_registration_state *state)
1455 if (ast_sip_push_task(state->client_state->serializer, unregister_task, state)) {
1463 static int queue_register(struct sip_outbound_registration_state *state)
1466 if (ast_sip_push_task(state->client_state->serializer, sip_outbound_registration_perform, state)) {
1474 static void unregister_all(void)
1476 struct ao2_container *states;
1478 states = ao2_global_obj_ref(current_states);
1483 /* Clean out all the states and let sorcery handle recreating the registrations */
1484 ao2_callback(states, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
1485 ao2_ref(states, -1);
1488 static void reregister_all(void)
1491 ast_sorcery_load_object(ast_sip_get_sorcery(), "registration");
1494 static char *cli_complete_registration(const char *line, const char *word,
1497 char *result = NULL;
1500 struct sip_outbound_registration *registration;
1501 struct ao2_container *registrations;
1502 struct ao2_iterator i;
1508 wordlen = strlen(word);
1509 if (wordlen == 0 && ++which > state) {
1510 return ast_strdup("*all");
1513 registrations = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration",
1514 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
1515 if (!registrations) {
1519 i = ao2_iterator_init(registrations, 0);
1520 while ((registration = ao2_iterator_next(&i))) {
1521 const char *name = ast_sorcery_object_get_id(registration);
1523 if (!strncasecmp(word, name, wordlen) && ++which > state) {
1524 result = ast_strdup(name);
1527 ao2_ref(registration, -1);
1532 ao2_iterator_destroy(&i);
1534 ao2_ref(registrations, -1);
1538 static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1540 struct sip_outbound_registration_state *state;
1541 const char *registration_name;
1545 e->command = "pjsip send unregister";
1547 "Usage: pjsip send unregister <registration> | *all\n"
1548 " Unregisters the specified (or all) outbound registration(s) "
1549 "and stops future registration attempts.\n";
1552 return cli_complete_registration(a->line, a->word, a->pos, a->n);
1556 return CLI_SHOWUSAGE;
1559 registration_name = a->argv[3];
1561 if (strcmp(registration_name, "*all") == 0) {
1563 ast_cli(a->fd, "Unregister all queued\n");
1567 state = get_state(registration_name);
1569 ast_cli(a->fd, "Unable to retrieve registration %s\n", registration_name);
1573 if (queue_unregister(state)) {
1574 ast_cli(a->fd, "Failed to queue unregistration\n");
1581 static char *cli_register(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1583 struct sip_outbound_registration_state *state;
1584 const char *registration_name;
1588 e->command = "pjsip send register";
1590 "Usage: pjsip send register <registration> | *all \n"
1591 " Unregisters the specified (or all) outbound "
1592 "registration(s) then starts registration(s) and schedules re-registrations.\n";
1595 return cli_complete_registration(a->line, a->word, a->pos, a->n);
1599 return CLI_SHOWUSAGE;
1602 registration_name = a->argv[3];
1604 if (strcmp(registration_name, "*all") == 0) {
1606 ast_cli(a->fd, "Re-register all queued\n");
1610 state = get_state(registration_name);
1612 ast_cli(a->fd, "Unable to retrieve registration %s\n", registration_name);
1616 /* We need to serialize the unregister and register so they need
1617 * to be queued as separate tasks.
1619 if (queue_unregister(state)) {
1620 ast_cli(a->fd, "Failed to queue unregistration\n");
1621 } else if (queue_register(state)) {
1622 ast_cli(a->fd, "Failed to queue registration\n");
1629 static int ami_unregister(struct mansession *s, const struct message *m)
1631 const char *registration_name = astman_get_header(m, "Registration");
1632 struct sip_outbound_registration_state *state;
1634 if (ast_strlen_zero(registration_name)) {
1635 astman_send_error(s, m, "Registration parameter missing.");
1639 if (strcmp(registration_name, "*all") == 0) {
1641 astman_send_ack(s, m, "Unregistrations queued.");
1645 state = get_state(registration_name);
1647 astman_send_error(s, m, "Unable to retrieve registration entry\n");
1651 if (queue_unregister(state)) {
1652 astman_send_ack(s, m, "Failed to queue unregistration");
1654 astman_send_ack(s, m, "Unregistration sent");
1661 static int ami_register(struct mansession *s, const struct message *m)
1663 const char *registration_name = astman_get_header(m, "Registration");
1664 struct sip_outbound_registration_state *state;
1666 if (ast_strlen_zero(registration_name)) {
1667 astman_send_error(s, m, "Registration parameter missing.");
1671 if (strcmp(registration_name, "*all") == 0) {
1673 astman_send_ack(s, m, "Reregistrations queued.");
1677 state = get_state(registration_name);
1679 astman_send_error(s, m, "Unable to retrieve registration entry\n");
1683 /* We need to serialize the unregister and register so they need
1684 * to be queued as separate tasks.
1686 if (queue_unregister(state)) {
1687 astman_send_ack(s, m, "Failed to queue unregistration");
1688 } else if (queue_register(state)) {
1689 astman_send_ack(s, m, "Failed to queue unregistration");
1691 astman_send_ack(s, m, "Reregistration sent");
1698 struct sip_ami_outbound {
1699 struct ast_sip_ami *ami;
1702 struct sip_outbound_registration *registration;
1705 static int ami_outbound_registration_task(void *obj)
1707 struct sip_ami_outbound *ami = obj;
1708 struct ast_str *buf;
1709 struct sip_outbound_registration_state *state;
1711 buf = ast_sip_create_ami_event("OutboundRegistrationDetail", ami->ami);
1716 ast_sip_sorcery_object_to_ami(ami->registration, &buf);
1718 if ((state = get_state(ast_sorcery_object_get_id(ami->registration)))) {
1719 pjsip_regc_info info;
1721 if (state->client_state->status == SIP_REGISTRATION_REGISTERED) {
1724 ++ami->not_registered;
1727 ast_str_append(&buf, 0, "Status: %s\r\n",
1728 sip_outbound_registration_status_str(state->client_state->status));
1730 pjsip_regc_get_info(state->client_state->client, &info);
1731 ast_str_append(&buf, 0, "NextReg: %d\r\n", info.next_reg);
1735 astman_append(ami->ami->s, "%s\r\n", ast_str_buffer(buf));
1738 return ast_sip_format_auths_ami(&ami->registration->outbound_auths, ami->ami);
1741 static int ami_outbound_registration_detail(void *obj, void *arg, int flags)
1743 struct sip_ami_outbound *ami = arg;
1745 ami->registration = obj;
1746 return ast_sip_push_task_synchronous(
1747 NULL, ami_outbound_registration_task, ami);
1750 static int ami_show_outbound_registrations(struct mansession *s,
1751 const struct message *m)
1753 struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), };
1754 struct sip_ami_outbound ami_outbound = { .ami = &ami };
1755 struct ao2_container *regs;
1757 regs = get_registrations();
1759 astman_send_error(s, m, "Unable to retrieve "
1760 "outbound registrations\n");
1764 astman_send_listack(s, m, "Following are Events for each Outbound registration",
1767 ao2_callback(regs, OBJ_NODATA, ami_outbound_registration_detail, &ami_outbound);
1769 astman_send_list_complete_start(s, m, "OutboundRegistrationDetailComplete",
1770 ami_outbound.registered + ami_outbound.not_registered);
1772 "Registered: %d\r\n"
1773 "NotRegistered: %d\r\n",
1774 ami_outbound.registered,
1775 ami_outbound.not_registered);
1776 astman_send_list_complete_end(s);
1782 static struct ao2_container *cli_get_container(const char *regex)
1784 RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
1785 struct ao2_container *s_container;
1787 container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "registration", regex);
1792 s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
1793 ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
1798 if (ao2_container_dup(s_container, container, 0)) {
1799 ao2_ref(s_container, -1);
1806 static int cli_iterator(void *container, ao2_callback_fn callback, void *args)
1808 ao2_callback(container, OBJ_NODATA, callback, args);
1813 static void *cli_retrieve_by_id(const char *id)
1815 struct ao2_container *states;
1816 void *obj = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration", id);
1819 /* if the object no longer exists then remove its state */
1820 states = ao2_global_obj_ref(current_states);
1822 ao2_find(states, id, OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA);
1823 ao2_ref(states, -1);
1830 static int cli_print_header(void *obj, void *arg, int flags)
1832 struct ast_sip_cli_context *context = arg;
1834 ast_assert(context->output_buffer != NULL);
1836 ast_str_append(&context->output_buffer, 0,
1837 " <Registration/ServerURI..............................> <Auth..........> <Status.......>\n");
1842 static int cli_print_body(void *obj, void *arg, int flags)
1844 struct sip_outbound_registration *registration = obj;
1845 struct ast_sip_cli_context *context = arg;
1846 const char *id = ast_sorcery_object_get_id(registration);
1847 struct sip_outbound_registration_state *state = get_state(id);
1848 #define REGISTRATION_URI_FIELD_LEN 53
1850 ast_assert(context->output_buffer != NULL);
1852 ast_str_append(&context->output_buffer, 0, " %-s/%-*.*s %-16s %-16s\n",
1854 (int) (REGISTRATION_URI_FIELD_LEN - strlen(id)),
1855 (int) (REGISTRATION_URI_FIELD_LEN - strlen(id)),
1856 registration->server_uri,
1857 AST_VECTOR_SIZE(®istration->outbound_auths)
1858 ? AST_VECTOR_GET(®istration->outbound_auths, 0)
1860 (state ? sip_outbound_registration_status_str(state->client_state->status) : "Unregistered"));
1863 if (context->show_details
1864 || (context->show_details_only_level_0 && context->indent_level == 0)) {
1865 ast_str_append(&context->output_buffer, 0, "\n");
1866 ast_sip_cli_print_sorcery_objectset(registration, context, 0);
1873 * A function pointer to callback needs to be within the
1874 * module in order to avoid problems with an undefined
1875 * symbol when the module is loaded.
1877 static char *my_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1879 return ast_sip_cli_traverse_objects(e, cmd, a);
1882 static struct ast_cli_entry cli_outbound_registration[] = {
1883 AST_CLI_DEFINE(cli_unregister, "Unregisters outbound registration target"),
1884 AST_CLI_DEFINE(cli_register, "Registers an outbound registration target"),
1885 AST_CLI_DEFINE(my_cli_traverse_objects, "List PJSIP Registrations",
1886 .command = "pjsip list registrations",
1887 .usage = "Usage: pjsip list registrations [ like <pattern> ]\n"
1888 " List the configured PJSIP Registrations\n"
1889 " Optional regular expression pattern is used to filter the list.\n"),
1890 AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Registrations",
1891 .command = "pjsip show registrations",
1892 .usage = "Usage: pjsip show registrations [ like <pattern> ]\n"
1893 " Show the configured PJSIP Registrations\n"
1894 " Optional regular expression pattern is used to filter the list.\n"),
1895 AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Registration",
1896 .command = "pjsip show registration",
1897 .usage = "Usage: pjsip show registration <id>\n"
1898 " Show the configured PJSIP Registration\n"),
1901 static struct ast_sip_cli_formatter_entry *cli_formatter;
1903 static void auth_observer(const char *type)
1905 struct sip_outbound_registration *registration;
1906 struct sip_outbound_registration_state *state;
1907 struct ao2_container *regs;
1908 const char *registration_id;
1909 struct ao2_iterator i;
1911 ast_debug(4, "Auths updated. Checking for any outbound registrations that are in permanent rejected state so they can be retried\n");
1913 regs = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration",
1914 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
1915 if (!regs || ao2_container_count(regs) == 0) {
1920 i = ao2_iterator_init(regs, 0);
1921 for (; (registration = ao2_iterator_next(&i)); ao2_ref(registration, -1)) {
1922 registration_id = ast_sorcery_object_get_id(registration);
1923 state = get_state(registration_id);
1924 if (state && state->client_state->status == SIP_REGISTRATION_REJECTED_PERMANENT) {
1925 ast_debug(4, "Trying outbound registration '%s' again\n", registration_id);
1927 if (ast_sip_push_task(state->client_state->serializer,
1928 sip_outbound_registration_perform, ao2_bump(state))) {
1929 ast_log(LOG_ERROR, "Failed to perform outbound registration on '%s'\n", registration_id);
1935 ao2_iterator_destroy(&i);
1939 static const struct ast_sorcery_observer observer_callbacks_auth = {
1940 .loaded = auth_observer,
1943 static int check_state(void *obj, void *arg, int flags)
1945 struct sip_outbound_registration_state *state = obj;
1946 struct sip_outbound_registration *registration;
1948 registration = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration",
1949 ast_sorcery_object_get_id(state->registration));
1950 if (!registration) {
1951 /* This is a dead registration */
1955 ao2_ref(registration, -1);
1961 * \brief Observer to purge dead registration states.
1963 * \param name Module name owning the sorcery instance.
1964 * \param sorcery Instance being observed.
1965 * \param object_type Name of object being observed.
1966 * \param reloaded Non-zero if the object is being reloaded.
1970 static void registration_loaded_observer(const char *name, const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
1972 struct ao2_container *states;
1974 if (strcmp(object_type, "registration")) {
1975 /* Not interested */
1979 states = ao2_global_obj_ref(current_states);
1981 /* Global container has gone. Likely shutting down. */
1986 * Refresh the current configured registrations. We don't need to hold
1987 * onto the objects, as the apply handler will cause their states to
1988 * be created appropriately.
1990 ao2_cleanup(get_registrations());
1992 /* Now to purge dead registrations. */
1993 ao2_callback(states, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, check_state, NULL);
1994 ao2_ref(states, -1);
1997 static const struct ast_sorcery_instance_observer observer_callbacks_registrations = {
1998 .object_type_loaded = registration_loaded_observer,
2001 static void registration_deleted_observer(const void *obj)
2003 const struct sip_outbound_registration *registration = obj;
2004 struct ao2_container *states;
2006 states = ao2_global_obj_ref(current_states);
2008 /* Global container has gone. Likely shutting down. */
2012 ao2_find(states, ast_sorcery_object_get_id(registration), OBJ_UNLINK | OBJ_NODATA | OBJ_SEARCH_KEY);
2014 ao2_ref(states, -1);
2017 static const struct ast_sorcery_observer registration_observer = {
2018 .deleted = registration_deleted_observer,
2021 static void network_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
2023 /* This callback is only concerned with network change messages from the system topic. */
2024 if (stasis_message_type(message) != ast_network_change_type()) {
2027 ast_debug(3, "Received network change event\n");
2032 static int unload_module(void)
2036 network_change_sub = stasis_unsubscribe_and_join(network_change_sub);
2038 ast_manager_unregister("PJSIPShowRegistrationsOutbound");
2039 ast_manager_unregister("PJSIPUnregister");
2040 ast_manager_unregister("PJSIPRegister");
2042 ast_cli_unregister_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
2043 ast_sip_unregister_cli_formatter(cli_formatter);
2044 cli_formatter = NULL;
2046 ast_sip_unregister_endpoint_identifier(&line_identifier);
2048 ast_sorcery_observer_remove(ast_sip_get_sorcery(), "auth", &observer_callbacks_auth);
2049 ast_sorcery_instance_observer_remove(ast_sip_get_sorcery(), &observer_callbacks_registrations);
2051 ast_sorcery_object_unregister(ast_sip_get_sorcery(), "registration");
2053 ao2_global_obj_release(current_states);
2055 /* Wait for registration serializers to get destroyed. */
2056 ast_debug(2, "Waiting for registration transactions to complete for unload.\n");
2057 remaining = ast_serializer_shutdown_group_join(shutdown_group, MAX_UNLOAD_TIMEOUT_TIME);
2060 * NOTE: We probably have a sip_outbound_registration_client_state
2061 * ref leak if the remaining count cannot reach zero after a few
2062 * minutes of trying to unload.
2064 ast_log(LOG_WARNING, "Unload incomplete. Could not stop %d outbound registrations. Try again later.\n",
2069 ast_debug(2, "Successful shutdown.\n");
2071 ao2_cleanup(shutdown_group);
2072 shutdown_group = NULL;
2077 static int load_module(void)
2079 struct ao2_container *new_states;
2081 CHECK_PJSIP_MODULE_LOADED();
2083 shutdown_group = ast_serializer_shutdown_group_alloc();
2084 if (!shutdown_group) {
2085 return AST_MODULE_LOAD_DECLINE;
2088 /* Create outbound registration states container. */
2089 new_states = ao2_container_alloc(DEFAULT_STATE_BUCKETS,
2090 registration_state_hash, registration_state_cmp);
2092 ast_log(LOG_ERROR, "Unable to allocate registration states container\n");
2094 return AST_MODULE_LOAD_DECLINE;
2096 ao2_global_obj_replace_unref(current_states, new_states);
2097 ao2_ref(new_states, -1);
2100 * Register sorcery object descriptions.
2102 ast_sorcery_apply_config(ast_sip_get_sorcery(), "res_pjsip_outbound_registration");
2103 ast_sorcery_apply_default(ast_sip_get_sorcery(), "registration", "config", "pjsip.conf,criteria=type=registration");
2105 if (ast_sorcery_object_register(ast_sip_get_sorcery(), "registration", sip_outbound_registration_alloc, NULL, sip_outbound_registration_apply)) {
2107 return AST_MODULE_LOAD_DECLINE;
2110 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "type", "", OPT_NOOP_T, 0, 0);
2111 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "server_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, server_uri));
2112 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "client_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, client_uri));
2113 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_user));
2114 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, transport));
2115 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, outbound_proxy));
2116 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, expiration));
2117 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_interval", "60", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_interval));
2118 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "forbidden_retry_interval", "0", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, forbidden_retry_interval));
2119 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "fatal_retry_interval", "0", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, fatal_retry_interval));
2120 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "max_retries", "10", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, max_retries));
2121 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "auth_rejection_permanent", "yes", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, auth_rejection_permanent));
2122 ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "registration", "outbound_auth", "", outbound_auth_handler, outbound_auths_to_str, outbound_auths_to_var_list, 0, 0);
2123 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, support_path));
2124 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "line", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, line));
2125 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, endpoint));
2128 * Register sorcery observers.
2130 if (ast_sorcery_instance_observer_add(ast_sip_get_sorcery(),
2131 &observer_callbacks_registrations)
2132 || ast_sorcery_observer_add(ast_sip_get_sorcery(), "auth",
2133 &observer_callbacks_auth)
2134 || ast_sorcery_observer_add(ast_sip_get_sorcery(), "registration",
2135 ®istration_observer)) {
2136 ast_log(LOG_ERROR, "Unable to register observers.\n");
2138 return AST_MODULE_LOAD_DECLINE;
2141 /* Register how this module identifies endpoints. */
2142 ast_sip_register_endpoint_identifier(&line_identifier);
2144 /* Register CLI commands. */
2145 cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
2146 if (!cli_formatter) {
2147 ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n");
2149 return AST_MODULE_LOAD_DECLINE;
2151 cli_formatter->name = "registration";
2152 cli_formatter->print_header = cli_print_header;
2153 cli_formatter->print_body = cli_print_body;
2154 cli_formatter->get_container = cli_get_container;
2155 cli_formatter->iterate = cli_iterator;
2156 cli_formatter->get_id = ast_sorcery_object_get_id;
2157 cli_formatter->retrieve_by_id = cli_retrieve_by_id;
2158 ast_sip_register_cli_formatter(cli_formatter);
2159 ast_cli_register_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
2161 /* Register AMI actions. */
2162 ast_manager_register_xml("PJSIPUnregister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_unregister);
2163 ast_manager_register_xml("PJSIPRegister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_register);
2164 ast_manager_register_xml("PJSIPShowRegistrationsOutbound", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_show_outbound_registrations);
2166 /* Clear any previous statsd gauges in case we weren't shutdown cleanly */
2167 ast_statsd_log("PJSIP.registrations.count", AST_STATSD_GAUGE, 0);
2168 ast_statsd_log("PJSIP.registrations.state.Registered", AST_STATSD_GAUGE, 0);
2169 ast_statsd_log("PJSIP.registrations.state.Unregistered", AST_STATSD_GAUGE, 0);
2170 ast_statsd_log("PJSIP.registrations.state.Rejected", AST_STATSD_GAUGE, 0);
2172 /* Load configuration objects */
2173 ast_sorcery_load_object(ast_sip_get_sorcery(), "registration");
2175 network_change_sub = stasis_subscribe(ast_system_topic(),
2176 network_change_stasis_cb, NULL);
2178 return AST_MODULE_LOAD_SUCCESS;
2181 static int reload_module(void)
2183 ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration");
2187 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Outbound Registration Support",
2188 .support_level = AST_MODULE_SUPPORT_CORE,
2189 .load = load_module,
2190 .reload = reload_module,
2191 .unload = unload_module,
2192 .load_pri = AST_MODPRI_APP_DEPEND,