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 "res_pjsip/include/res_pjsip_private.h"
39 <configInfo name="res_pjsip_outbound_registration" language="en_US">
40 <synopsis>SIP resource for outbound registrations</synopsis>
42 <emphasis>Outbound Registration</emphasis>
44 <para>This module allows <literal>res_pjsip</literal> to register to other SIP servers.</para>
46 <configFile name="pjsip.conf">
47 <configObject name="registration">
48 <synopsis>The configuration for outbound registration</synopsis>
50 Registration is <emphasis>COMPLETELY</emphasis> separate from the rest of
51 <literal>pjsip.conf</literal>. A minimal configuration consists of
52 setting a <literal>server_uri</literal> and a <literal>client_uri</literal>.
54 <configOption name="auth_rejection_permanent" default="yes">
55 <synopsis>Determines whether failed authentication challenges are treated
56 as permanent failures.</synopsis>
57 <description><para>If this option is enabled and an authentication challenge fails,
58 registration will not be attempted again until the configuration is reloaded.</para></description>
60 <configOption name="client_uri">
61 <synopsis>Client SIP URI used when attemping outbound registration</synopsis>
63 This is the address-of-record for the outbound registration (i.e. the URI in
64 the To header of the REGISTER).</para>
65 <para>For registration with an ITSP, the client SIP URI may need to consist of
66 an account name or number and the provider's hostname for their registrar, e.g.
67 client_uri=1234567890@example.com. This may differ between providers.</para>
68 <para>For registration to generic registrars, the client SIP URI will depend
69 on networking specifics and configuration of the registrar.
72 <configOption name="contact_user">
73 <synopsis>Contact User to use in request</synopsis>
75 <configOption name="expiration" default="3600">
76 <synopsis>Expiration time for registrations in seconds</synopsis>
78 <configOption name="max_retries" default="10">
79 <synopsis>Maximum number of registration attempts.</synopsis>
81 <configOption name="outbound_auth" default="">
82 <synopsis>Authentication object to be used for outbound registrations.</synopsis>
84 <configOption name="outbound_proxy" default="">
85 <synopsis>Outbound Proxy used to send registrations</synopsis>
87 <configOption name="retry_interval" default="60">
88 <synopsis>Interval in seconds between retries if outbound registration is unsuccessful</synopsis>
90 <configOption name="forbidden_retry_interval" default="0">
91 <synopsis>Interval used when receiving a 403 Forbidden response.</synopsis>
93 If a 403 Forbidden is received, chan_pjsip will wait
94 <replaceable>forbidden_retry_interval</replaceable> seconds before
95 attempting registration again. If 0 is specified, chan_pjsip will not
96 retry after receiving a 403 Forbidden response. Setting this to a non-zero
97 value goes against a "SHOULD NOT" in RFC3261, but can be used to work around
101 <configOption name="server_uri">
102 <synopsis>SIP URI of the server to register against</synopsis>
104 This is the URI at which to find the registrar to send the outbound REGISTER. This URI
105 is used as the request URI of the outbound REGISTER request from Asterisk.</para>
106 <para>For registration with an ITSP, the setting may often be just the domain of
107 the registrar, e.g. sip:sip.example.com.
108 </para></description>
110 <configOption name="transport">
111 <synopsis>Transport used for outbound authentication</synopsis>
113 <note><para>A <replaceable>transport</replaceable> configured in
114 <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>
117 <configOption name="line">
118 <synopsis>Whether to add a 'line' parameter to the Contact for inbound call matching</synopsis>
120 When enabled this option will cause a 'line' parameter to be added to the Contact
121 header placed into the outgoing registration request. If the remote server sends a call
122 this line parameter will be used to establish a relationship to the outbound registration,
123 ultimately causing the configured endpoint to be used.
124 </para></description>
126 <configOption name="endpoint">
127 <synopsis>Endpoint to use for incoming related calls</synopsis>
129 When line support is enabled this configured endpoint name is used for incoming calls
130 that are related to the outbound registration.
131 </para></description>
133 <configOption name="type">
134 <synopsis>Must be of type 'registration'.</synopsis>
136 <configOption name="support_path">
137 <synopsis>Enables Path support for outbound REGISTER requests.</synopsis>
139 When this option is enabled, outbound REGISTER requests will advertise
140 support for Path headers so that intervening proxies can add to the Path
142 </para></description>
147 <manager name="PJSIPUnregister" language="en_US">
149 Unregister an outbound registration.
152 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
153 <parameter name="Registration" required="true">
154 <para>The outbound registration to unregister.</para>
159 Unregisters the specified outbound registration and stops future registration attempts.
160 Call PJSIPRegister to start registration and schedule re-registrations according to configuration.
164 <manager name="PJSIPRegister" language="en_US">
166 Register an outbound registration.
169 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
170 <parameter name="Registration" required="true">
171 <para>The outbound registration to register.</para>
176 Unregisters the specified outbound registration then starts registration and schedules re-registrations
177 according to configuration.
178 future registrations.
182 <manager name="PJSIPShowRegistrationsOutbound" language="en_US">
184 Lists PJSIP outbound registrations.
189 In response <literal>OutboundRegistrationDetail</literal> events showing configuration and status
190 information are raised for each outbound registration object. <literal>AuthDetail</literal>
191 events are raised for each associated auth object as well. Once all events are completed an
192 <literal>OutboundRegistrationDetailComplete</literal> is issued.
198 /*! \brief Amount of buffer time (in seconds) before expiration that we re-register at */
199 #define REREGISTER_BUFFER_TIME 10
201 /*! \brief Size of the buffer for creating a unique string for the line */
202 #define LINE_PARAMETER_SIZE 8
204 /*! \brief Various states that an outbound registration may be in */
205 enum sip_outbound_registration_status {
206 /*! \brief Currently unregistered */
207 SIP_REGISTRATION_UNREGISTERED = 0,
208 /*! \brief Registered, yay! */
209 SIP_REGISTRATION_REGISTERED,
210 /*! \brief Registration was rejected, but response was temporal */
211 SIP_REGISTRATION_REJECTED_TEMPORARY,
212 /*! \brief Registration was rejected, permanently */
213 SIP_REGISTRATION_REJECTED_PERMANENT,
214 /*! \brief Registration has been stopped */
215 SIP_REGISTRATION_STOPPED,
218 static const char *sip_outbound_registration_status_str[] = {
219 [SIP_REGISTRATION_UNREGISTERED] = "Unregistered",
220 [SIP_REGISTRATION_REGISTERED] = "Registered",
221 [SIP_REGISTRATION_REJECTED_TEMPORARY] = "Rejected",
222 [SIP_REGISTRATION_REJECTED_PERMANENT] = "Rejected",
223 [SIP_REGISTRATION_STOPPED] = "Stopped",
226 /*! \brief Outbound registration information */
227 struct sip_outbound_registration {
228 /*! \brief Sorcery object details */
229 SORCERY_OBJECT(details);
230 /*! \brief Stringfields */
231 AST_DECLARE_STRING_FIELDS(
232 /*! \brief URI for the registrar */
233 AST_STRING_FIELD(server_uri);
234 /*! \brief URI for the AOR */
235 AST_STRING_FIELD(client_uri);
236 /*! \brief Optional user for contact header */
237 AST_STRING_FIELD(contact_user);
238 /*! \brief Explicit transport to use for registration */
239 AST_STRING_FIELD(transport);
240 /*! \brief Outbound proxy to use */
241 AST_STRING_FIELD(outbound_proxy);
242 /*! \brief Endpoint to use for related incoming calls */
243 AST_STRING_FIELD(endpoint);
245 /*! \brief Requested expiration time */
246 unsigned int expiration;
247 /*! \brief Interval at which retries should occur for temporal responses */
248 unsigned int retry_interval;
249 /*! \brief Interval at which retries should occur for permanent responses */
250 unsigned int forbidden_retry_interval;
251 /*! \brief Treat authentication challenges that we cannot handle as permanent failures */
252 unsigned int auth_rejection_permanent;
253 /*! \brief Maximum number of retries permitted */
254 unsigned int max_retries;
255 /*! \brief Whether to add a line parameter to the outbound Contact or not */
257 /*! \brief Configured authentication credentials */
258 struct ast_sip_auth_vector outbound_auths;
259 /*! \brief Whether Path support is enabled */
260 unsigned int support_path;
263 /*! \brief Outbound registration client state information (persists for lifetime of regc) */
264 struct sip_outbound_registration_client_state {
265 /*! \brief Current status of this registration */
266 enum sip_outbound_registration_status status;
267 /*! \brief Outbound registration client */
269 /*! \brief Timer entry for retrying on temporal responses */
270 pj_timer_entry timer;
271 /*! \brief Optional line parameter placed into Contact */
272 char line[LINE_PARAMETER_SIZE];
273 /*! \brief Current number of retries */
274 unsigned int retries;
275 /*! \brief Maximum number of retries permitted */
276 unsigned int max_retries;
277 /*! \brief Interval at which retries should occur for temporal responses */
278 unsigned int retry_interval;
279 /*! \brief Interval at which retries should occur for permanent responses */
280 unsigned int forbidden_retry_interval;
281 /*! \brief Treat authentication challenges that we cannot handle as permanent failures */
282 unsigned int auth_rejection_permanent;
283 /*! \brief Determines whether SIP Path support should be advertised */
284 unsigned int support_path;
285 /*! \brief Serializer for stuff and things */
286 struct ast_taskprocessor *serializer;
287 /*! \brief Configured authentication credentials */
288 struct ast_sip_auth_vector outbound_auths;
289 /*! \brief Registration should be destroyed after completion of transaction */
290 unsigned int destroy:1;
291 /*! \brief Non-zero if we have attempted sending a REGISTER with authentication */
292 unsigned int auth_attempted:1;
295 /*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
296 struct sip_outbound_registration_state {
297 /*! \brief Outbound registration configuration object */
298 struct sip_outbound_registration *registration;
299 /*! \brief Client state information */
300 struct sip_outbound_registration_client_state *client_state;
303 /*! \brief Default number of state container buckets */
304 #define DEFAULT_STATE_BUCKETS 53
305 static AO2_GLOBAL_OBJ_STATIC(current_states);
307 /*! \brief hashing function for state objects */
308 static int registration_state_hash(const void *obj, const int flags)
310 const struct sip_outbound_registration_state *object;
313 switch (flags & OBJ_SEARCH_MASK) {
317 case OBJ_SEARCH_OBJECT:
319 key = ast_sorcery_object_get_id(object->registration);
325 return ast_str_hash(key);
328 /*! \brief comparator function for state objects */
329 static int registration_state_cmp(void *obj, void *arg, int flags)
331 const struct sip_outbound_registration_state *object_left = obj;
332 const struct sip_outbound_registration_state *object_right = arg;
333 const char *right_key = arg;
336 switch (flags & OBJ_SEARCH_MASK) {
337 case OBJ_SEARCH_OBJECT:
338 right_key = ast_sorcery_object_get_id(object_right->registration);
341 cmp = strcmp(ast_sorcery_object_get_id(object_left->registration), right_key);
343 case OBJ_SEARCH_PARTIAL_KEY:
344 /* Not supported by container. */
357 static struct sip_outbound_registration_state *get_state(const char *id)
359 RAII_VAR(struct ao2_container *, states,
360 ao2_global_obj_ref(current_states), ao2_cleanup);
361 return states ? ao2_find(states, id, OBJ_SEARCH_KEY) : NULL;
364 static int registration_state_add(void *obj, void *arg, int flags)
366 struct sip_outbound_registration_state *state =
367 get_state(ast_sorcery_object_get_id(obj));
370 ao2_link(arg, state);
377 static struct ao2_container *get_registrations(void)
379 RAII_VAR(struct ao2_container *, new_states, NULL, ao2_cleanup);
380 struct ao2_container *registrations = ast_sorcery_retrieve_by_fields(
381 ast_sip_get_sorcery(), "registration",
382 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
384 if (!(new_states = ao2_container_alloc(DEFAULT_STATE_BUCKETS,
385 registration_state_hash, registration_state_cmp))) {
386 ast_log(LOG_ERROR, "Unable to allocate registration states container\n");
390 if (registrations && ao2_container_count(registrations)) {
391 ao2_callback(registrations, OBJ_NODATA, registration_state_add, new_states);
394 ao2_global_obj_replace_unref(current_states, new_states);
395 return registrations;
398 /*! \brief Callback function for matching an outbound registration based on line */
399 static int line_identify_relationship(void *obj, void *arg, int flags)
401 struct sip_outbound_registration_state *state = obj;
402 pjsip_param *line = arg;
404 return !pj_strcmp2(&line->value, state->client_state->line) ? CMP_MATCH | CMP_STOP : 0;
407 /*! \brief Endpoint identifier which uses the 'line' parameter to establish a relationship to an outgoing registration */
408 static struct ast_sip_endpoint *line_identify(pjsip_rx_data *rdata)
411 static const pj_str_t LINE_STR = { "line", 4 };
413 RAII_VAR(struct ao2_container *, states, NULL, ao2_cleanup);
414 RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
416 if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) {
419 uri = pjsip_uri_get_uri(rdata->msg_info.to->uri);
421 line = pjsip_param_find(&uri->other_param, &LINE_STR);
426 states = ao2_global_obj_ref(current_states);
431 state = ao2_callback(states, 0, line_identify_relationship, line);
432 if (!state || ast_strlen_zero(state->registration->endpoint)) {
436 ast_debug(3, "Determined relationship to outbound registration '%s' based on line '%s', using configured endpoint '%s'\n",
437 ast_sorcery_object_get_id(state->registration), state->client_state->line, state->registration->endpoint);
439 return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", state->registration->endpoint);
442 static struct ast_sip_endpoint_identifier line_identifier = {
443 .identify_endpoint = line_identify,
446 /*! \brief Helper function which cancels the timer on a client */
447 static void cancel_registration(struct sip_outbound_registration_client_state *client_state)
449 if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &client_state->timer)) {
450 /* The timer was successfully cancelled, drop the refcount of client_state */
451 ao2_ref(client_state, -1);
455 static pj_str_t PATH_NAME = { "path", 4 };
457 /*! \brief Callback function for registering */
458 static int handle_client_registration(void *data)
460 RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
461 pjsip_tx_data *tdata;
462 pjsip_regc_info info;
463 char server_uri[PJSIP_MAX_URL_SIZE], client_uri[PJSIP_MAX_URL_SIZE];
465 ao2_ref(client_state, -1);
467 if ((client_state->status == SIP_REGISTRATION_STOPPED) ||
468 (pjsip_regc_register(client_state->client, PJ_FALSE, &tdata) != PJ_SUCCESS)) {
472 pjsip_regc_get_info(client_state->client, &info);
473 ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
474 ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
475 ast_debug(3, "REGISTER attempt %u to '%s' with client '%s'\n",
476 client_state->retries + 1, server_uri, client_uri);
478 if (client_state->support_path) {
479 pjsip_supported_hdr *hdr;
481 hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL);
483 /* insert a new Supported header */
484 hdr = pjsip_supported_hdr_create(tdata->pool);
489 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
492 /* add on to the existing Supported header */
493 pj_strassign(&hdr->values[hdr->count++], &PATH_NAME);
496 /* Due to the registration the callback may now get called, so bump the ref count */
497 ao2_ref(client_state, +1);
498 if (pjsip_regc_send(client_state->client, tdata) != PJ_SUCCESS) {
499 ao2_ref(client_state, -1);
505 /*! \brief Timer callback function, used just for registrations */
506 static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
508 struct sip_outbound_registration_client_state *client_state = entry->user_data;
510 ao2_ref(client_state, +1);
511 if (ast_sip_push_task(client_state->serializer, handle_client_registration, client_state)) {
512 ast_log(LOG_WARNING, "Failed to pass outbound registration to threadpool\n");
513 ao2_ref(client_state, -1);
519 /*! \brief Helper function which sets up the timer to re-register in a specific amount of time */
520 static void schedule_registration(struct sip_outbound_registration_client_state *client_state, unsigned int seconds)
522 pj_time_val delay = { .sec = seconds, };
524 cancel_registration(client_state);
526 ao2_ref(client_state, +1);
527 if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &client_state->timer, &delay) != PJ_SUCCESS) {
528 ast_log(LOG_WARNING, "Failed to pass timed registration to scheduler\n");
529 ao2_ref(client_state, -1);
533 /*! \brief Callback function for unregistering (potentially) and destroying state */
534 static int handle_client_state_destruction(void *data)
536 RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
538 cancel_registration(client_state);
540 if (client_state->client) {
541 pjsip_regc_info info;
543 pjsip_regc_get_info(client_state->client, &info);
545 if (info.is_busy == PJ_TRUE) {
546 /* If a client transaction is in progress we defer until it is complete */
547 client_state->destroy = 1;
551 if (client_state->status != SIP_REGISTRATION_UNREGISTERED && client_state->status != SIP_REGISTRATION_REJECTED_PERMANENT) {
552 pjsip_tx_data *tdata;
554 if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS) {
555 pjsip_regc_send(client_state->client, tdata);
559 pjsip_regc_destroy(client_state->client);
562 client_state->status = SIP_REGISTRATION_STOPPED;
563 ast_sip_auth_vector_destroy(&client_state->outbound_auths);
568 /*! \brief Structure for registration response */
569 struct registration_response {
570 /*! \brief Response code for the registration attempt */
572 /*! \brief Expiration time for registration */
574 /*! \brief Retry-After value */
576 /*! \brief Outbound registration client state */
577 struct sip_outbound_registration_client_state *client_state;
578 /*! \brief The response message */
579 pjsip_rx_data *rdata;
580 /*! \brief The response transaction */
581 pjsip_transaction *tsx;
584 /*! \brief Registration response structure destructor */
585 static void registration_response_destroy(void *obj)
587 struct registration_response *response = obj;
589 if (response->rdata) {
590 pjsip_rx_data_free_cloned(response->rdata);
593 ao2_cleanup(response->client_state);
596 /* \brief Helper funtion which determines if a response code is temporal or not */
597 static int sip_outbound_registration_is_temporal(unsigned int code,
598 struct sip_outbound_registration_client_state *client_state)
600 /* Shamelessly taken from pjsua */
601 if (code == PJSIP_SC_REQUEST_TIMEOUT ||
602 code == PJSIP_SC_INTERNAL_SERVER_ERROR ||
603 code == PJSIP_SC_BAD_GATEWAY ||
604 code == PJSIP_SC_SERVICE_UNAVAILABLE ||
605 code == PJSIP_SC_SERVER_TIMEOUT ||
606 ((code == PJSIP_SC_UNAUTHORIZED ||
607 code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED) &&
608 !client_state->auth_rejection_permanent) ||
609 PJSIP_IS_STATUS_IN_CLASS(code, 600)) {
616 static void schedule_retry(struct registration_response *response, unsigned int interval,
617 const char *server_uri, const char *client_uri)
619 response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
620 schedule_registration(response->client_state, interval);
622 if (response->rdata) {
623 ast_log(LOG_WARNING, "Temporal response '%d' received from '%s' on "
624 "registration attempt to '%s', retrying in '%u'\n",
625 response->code, server_uri, client_uri, interval);
627 ast_log(LOG_WARNING, "No response received from '%s' on "
628 "registration attempt to '%s', retrying in '%u'\n",
629 server_uri, client_uri, interval);
633 /*! \brief Callback function for handling a response to a registration attempt */
634 static int handle_registration_response(void *data)
636 RAII_VAR(struct registration_response *, response, data, ao2_cleanup);
637 pjsip_regc_info info;
638 char server_uri[PJSIP_MAX_URL_SIZE], client_uri[PJSIP_MAX_URL_SIZE];
640 if (response->client_state->status == SIP_REGISTRATION_STOPPED) {
644 pjsip_regc_get_info(response->client_state->client, &info);
645 ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
646 ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
648 if (!response->client_state->auth_attempted &&
649 (response->code == 401 || response->code == 407)) {
650 pjsip_tx_data *tdata;
651 if (!ast_sip_create_request_with_auth(&response->client_state->outbound_auths,
652 response->rdata, response->tsx, &tdata)) {
653 ao2_ref(response->client_state, +1);
654 response->client_state->auth_attempted = 1;
655 if (pjsip_regc_send(response->client_state->client, tdata) != PJ_SUCCESS) {
656 response->client_state->auth_attempted = 0;
657 ao2_cleanup(response->client_state);
661 /* Otherwise, fall through so the failure is processed appropriately */
664 response->client_state->auth_attempted = 0;
666 if (PJSIP_IS_STATUS_IN_CLASS(response->code, 200)) {
667 /* Check if this is in regards to registering or unregistering */
668 if (response->expiration) {
669 /* If the registration went fine simply reschedule registration for the future */
670 ast_debug(1, "Outbound registration to '%s' with client '%s' successful\n", server_uri, client_uri);
671 response->client_state->status = SIP_REGISTRATION_REGISTERED;
672 response->client_state->retries = 0;
673 schedule_registration(response->client_state, response->expiration - REREGISTER_BUFFER_TIME);
675 ast_debug(1, "Outbound unregistration to '%s' with client '%s' successful\n", server_uri, client_uri);
676 response->client_state->status = SIP_REGISTRATION_UNREGISTERED;
678 } else if (response->retry_after) {
679 /* If we have been instructed to retry after a period of time, schedule it as such */
680 schedule_retry(response, response->retry_after, server_uri, client_uri);
681 } else if (response->client_state->retry_interval && sip_outbound_registration_is_temporal(response->code, response->client_state)) {
682 if (response->client_state->retries == response->client_state->max_retries) {
683 /* If we received enough temporal responses to exceed our maximum give up permanently */
684 response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
685 ast_log(LOG_WARNING, "Maximum retries reached when attempting outbound registration to '%s' with client '%s', stopping registration attempt\n",
686 server_uri, client_uri);
688 /* On the other hand if we can still try some more do so */
689 response->client_state->retries++;
690 schedule_retry(response, response->client_state->retry_interval, server_uri, client_uri);
693 if (response->code == 403
694 && response->client_state->forbidden_retry_interval
695 && response->client_state->retries < response->client_state->max_retries) {
696 /* A forbidden response retry interval is configured and there are retries remaining */
697 response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
698 response->client_state->retries++;
699 schedule_registration(response->client_state, response->client_state->forbidden_retry_interval);
700 ast_log(LOG_WARNING, "403 Forbidden fatal response received from '%s' on registration attempt to '%s', retrying in '%u' seconds\n",
701 server_uri, client_uri, response->client_state->forbidden_retry_interval);
703 /* Finally if there's no hope of registering give up */
704 response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
705 if (response->rdata) {
706 ast_log(LOG_WARNING, "Fatal response '%d' received from '%s' on registration attempt to '%s', stopping outbound registration\n",
707 response->code, server_uri, client_uri);
709 ast_log(LOG_WARNING, "Fatal registration attempt to '%s', stopping outbound registration\n", client_uri);
714 ast_system_publish_registry("PJSIP", client_uri, server_uri, sip_outbound_registration_status_str[response->client_state->status], NULL);
716 /* If deferred destruction is in use see if we need to destroy now */
717 if (response->client_state->destroy) {
718 handle_client_state_destruction(response->client_state);
724 /*! \brief Callback function for outbound registration client */
725 static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *param)
727 RAII_VAR(struct sip_outbound_registration_client_state *, client_state, param->token, ao2_cleanup);
728 struct registration_response *response = ao2_alloc(sizeof(*response), registration_response_destroy);
730 response->code = param->code;
731 response->expiration = param->expiration;
732 response->client_state = client_state;
733 ao2_ref(response->client_state, +1);
736 struct pjsip_retry_after_hdr *retry_after = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_RETRY_AFTER, NULL);
738 response->retry_after = retry_after ? retry_after->ivalue : 0;
739 response->tsx = pjsip_rdata_get_tsx(param->rdata);
740 pjsip_rx_data_clone(param->rdata, 0, &response->rdata);
743 if (ast_sip_push_task(client_state->serializer, handle_registration_response, response)) {
744 ast_log(LOG_WARNING, "Failed to pass incoming registration response to threadpool\n");
745 ao2_cleanup(response);
749 /*! \brief Destructor function for registration state */
750 static void sip_outbound_registration_state_destroy(void *obj)
752 struct sip_outbound_registration_state *state = obj;
754 ao2_cleanup(state->registration);
756 if (!state->client_state) {
760 if (state->client_state->serializer && ast_sip_push_task(state->client_state->serializer, handle_client_state_destruction, state->client_state)) {
761 ast_log(LOG_WARNING, "Failed to pass outbound registration client destruction to threadpool\n");
762 ao2_ref(state->client_state, -1);
766 /*! \brief Destructor function for client registration state */
767 static void sip_outbound_registration_client_state_destroy(void *obj)
769 struct sip_outbound_registration_client_state *client_state = obj;
771 ast_taskprocessor_unreference(client_state->serializer);
774 /*! \brief Allocator function for registration state */
775 static struct sip_outbound_registration_state *sip_outbound_registration_state_alloc(struct sip_outbound_registration *registration)
777 struct sip_outbound_registration_state *state = ao2_alloc(sizeof(*state), sip_outbound_registration_state_destroy);
779 if (!state || !(state->client_state = ao2_alloc(sizeof(*state->client_state), sip_outbound_registration_client_state_destroy))) {
784 if (!(state->client_state->serializer = ast_sip_create_serializer())) {
785 ao2_cleanup(state->client_state);
790 state->client_state->status = SIP_REGISTRATION_UNREGISTERED;
791 state->client_state->timer.user_data = state->client_state;
792 state->client_state->timer.cb = sip_outbound_registration_timer_cb;
794 state->registration = ao2_bump(registration);
798 /*! \brief Destructor function for registration information */
799 static void sip_outbound_registration_destroy(void *obj)
801 struct sip_outbound_registration *registration = obj;
803 ast_sip_auth_vector_destroy(®istration->outbound_auths);
805 ast_string_field_free_memory(registration);
808 /*! \brief Allocator function for registration information */
809 static void *sip_outbound_registration_alloc(const char *name)
811 struct sip_outbound_registration *registration = ast_sorcery_generic_alloc(sizeof(*registration), sip_outbound_registration_destroy);
812 if (!registration || ast_string_field_init(registration, 256)) {
813 ao2_cleanup(registration);
820 /*! \brief Helper function which populates a pj_str_t with a contact header */
821 static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const char *user, const pj_str_t *target, pjsip_tpselector *selector,
824 pj_str_t tmp, local_addr;
826 pjsip_sip_uri *sip_uri;
827 pjsip_transport_type_e type = PJSIP_TRANSPORT_UNSPECIFIED;
830 pj_strdup_with_null(pool, &tmp, target);
832 if (!(uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0)) ||
833 (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))) {
837 sip_uri = pjsip_uri_get_uri(uri);
839 if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri)) {
840 type = PJSIP_TRANSPORT_TLS;
841 } else if (!sip_uri->transport_param.slen) {
842 type = PJSIP_TRANSPORT_UDP;
844 type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
847 if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
851 if (pj_strchr(&sip_uri->host, ':')) {
852 type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
855 if (pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), pool, type, selector,
856 &local_addr, &local_port) != PJ_SUCCESS) {
860 if (!pj_strchr(&sip_uri->host, ':') && pj_strchr(&local_addr, ':')) {
861 type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
864 contact->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
865 contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
866 "<%s:%s@%s%.*s%s:%d%s%s%s%s>",
867 (pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE) ? "sips" : "sip",
869 (type & PJSIP_TRANSPORT_IPV6) ? "[" : "",
870 (int)local_addr.slen,
872 (type & PJSIP_TRANSPORT_IPV6) ? "]" : "",
874 (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? ";transport=" : "",
875 (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? pjsip_transport_get_type_name(type) : "",
876 !ast_strlen_zero(line) ? ";line=" : "",
884 * \brief Check if a registration can be reused
886 * This checks if the existing outbound registration's configuration differs from a newly-applied
887 * outbound registration to see if the applied one.
889 * \param existing The pre-existing outbound registration
890 * \param applied The newly-created registration
892 static int can_reuse_registration(struct sip_outbound_registration *existing,
893 struct sip_outbound_registration *applied)
896 struct ast_sorcery *sorcery = ast_sip_get_sorcery();
897 struct ast_variable *ve = ast_sorcery_objectset_create(sorcery, existing);
898 struct ast_variable *va = ast_sorcery_objectset_create(sorcery, applied);
899 struct ast_variable *vc = NULL;
901 if (ast_sorcery_changeset_create(ve, va, &vc) || vc != NULL) {
903 ast_debug(4, "Registration '%s' changed. Can't re-use.\n", ast_sorcery_object_get_id(existing));
905 ast_debug(4, "Registration '%s' didn't change. Can re-use\n", ast_sorcery_object_get_id(existing));
908 ast_variables_destroy(ve);
909 ast_variables_destroy(va);
910 ast_variables_destroy(vc);
915 /*! \brief Helper function that allocates a pjsip registration client and configures it */
916 static int sip_outbound_registration_regc_alloc(void *data)
918 struct sip_outbound_registration_state *state = data;
919 RAII_VAR(struct sip_outbound_registration *, registration,
920 ao2_bump(state->registration), ao2_cleanup);
924 pj_str_t server_uri, client_uri, contact_uri;
925 pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
927 pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "URI Validation", 256, 256);
929 ast_log(LOG_ERROR, "Could not create pool for URI validation on outbound registration '%s'\n",
930 ast_sorcery_object_get_id(registration));
934 pj_strdup2_with_null(pool, &tmp, registration->server_uri);
935 uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
937 ast_log(LOG_ERROR, "Invalid server URI '%s' specified on outbound registration '%s'\n",
938 registration->server_uri, ast_sorcery_object_get_id(registration));
939 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
943 pj_strdup2_with_null(pool, &tmp, registration->client_uri);
944 uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
946 ast_log(LOG_ERROR, "Invalid client URI '%s' specified on outbound registration '%s'\n",
947 registration->client_uri, ast_sorcery_object_get_id(registration));
948 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
952 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
954 if (!ast_strlen_zero(registration->transport)) {
955 RAII_VAR(struct ast_sip_transport *, transport, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", registration->transport), ao2_cleanup);
957 if (!transport || !transport->state) {
958 ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport '%s' "
959 " for outbound registration", registration->transport);
963 if (transport->state->transport) {
964 selector.type = PJSIP_TPSELECTOR_TRANSPORT;
965 selector.u.transport = transport->state->transport;
966 } else if (transport->state->factory) {
967 selector.type = PJSIP_TPSELECTOR_LISTENER;
968 selector.u.listener = transport->state->factory;
974 if (!state->client_state->client &&
975 pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state->client_state, sip_outbound_registration_response_cb,
976 &state->client_state->client) != PJ_SUCCESS) {
980 pjsip_regc_set_transport(state->client_state->client, &selector);
982 if (!ast_strlen_zero(registration->outbound_proxy)) {
983 pjsip_route_hdr route_set, *route;
984 static const pj_str_t ROUTE_HNAME = { "Route", 5 };
987 pj_list_init(&route_set);
989 pj_strdup2_with_null(pjsip_regc_get_pool(state->client_state->client), &tmp, registration->outbound_proxy);
990 if (!(route = pjsip_parse_hdr(pjsip_regc_get_pool(state->client_state->client), &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL))) {
993 pj_list_insert_nodes_before(&route_set, route);
995 pjsip_regc_set_route_set(state->client_state->client, &route_set);
998 if (state->registration->line) {
999 ast_generate_random_string(state->client_state->line, sizeof(state->client_state->line));
1002 pj_cstr(&server_uri, registration->server_uri);
1005 if (sip_dialog_create_contact(pjsip_regc_get_pool(state->client_state->client), &contact_uri, S_OR(registration->contact_user, "s"), &server_uri, &selector,
1006 state->client_state->line)) {
1010 pj_cstr(&client_uri, registration->client_uri);
1011 if (pjsip_regc_init(state->client_state->client, &server_uri, &client_uri, &client_uri, 1, &contact_uri, registration->expiration) != PJ_SUCCESS) {
1018 /*! \brief Helper function which performs a single registration */
1019 static int sip_outbound_registration_perform(void *data)
1021 RAII_VAR(struct sip_outbound_registration_state *, state, data, ao2_cleanup);
1022 RAII_VAR(struct sip_outbound_registration *, registration, ao2_bump(state->registration), ao2_cleanup);
1025 /* Just in case the client state is being reused for this registration, free the auth information */
1026 ast_sip_auth_vector_destroy(&state->client_state->outbound_auths);
1028 AST_VECTOR_INIT(&state->client_state->outbound_auths, AST_VECTOR_SIZE(®istration->outbound_auths));
1029 for (i = 0; i < AST_VECTOR_SIZE(®istration->outbound_auths); ++i) {
1030 const char *name = ast_strdup(AST_VECTOR_GET(®istration->outbound_auths, i));
1031 AST_VECTOR_APPEND(&state->client_state->outbound_auths, name);
1033 state->client_state->retry_interval = registration->retry_interval;
1034 state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval;
1035 state->client_state->max_retries = registration->max_retries;
1036 state->client_state->retries = 0;
1037 state->client_state->support_path = registration->support_path;
1038 state->client_state->auth_rejection_permanent = registration->auth_rejection_permanent;
1040 pjsip_regc_update_expires(state->client_state->client, registration->expiration);
1042 schedule_registration(state->client_state, (ast_random() % 10) + 1);
1047 /*! \brief Apply function which finds or allocates a state structure */
1048 static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, void *obj)
1050 RAII_VAR(struct ao2_container *, states, ao2_global_obj_ref(current_states), ao2_cleanup);
1051 RAII_VAR(struct sip_outbound_registration_state *, state,
1052 ao2_find(states, ast_sorcery_object_get_id(obj), OBJ_SEARCH_KEY), ao2_cleanup);
1053 RAII_VAR(struct sip_outbound_registration_state *, new_state, NULL, ao2_cleanup);
1054 struct sip_outbound_registration *applied = obj;
1056 ast_debug(4, "Applying configuration to outbound registration '%s'\n", ast_sorcery_object_get_id(applied));
1058 if (ast_strlen_zero(applied->server_uri)) {
1059 ast_log(LOG_ERROR, "No server URI specified on outbound registration '%s'",
1060 ast_sorcery_object_get_id(applied));
1062 } else if (ast_strlen_zero(applied->client_uri)) {
1063 ast_log(LOG_ERROR, "No client URI specified on outbound registration '%s'\n",
1064 ast_sorcery_object_get_id(applied));
1066 } else if (applied->line && ast_strlen_zero(applied->endpoint)) {
1067 ast_log(LOG_ERROR, "Line support has been enabled on outbound registration '%s' without providing an endpoint\n",
1068 ast_sorcery_object_get_id(applied));
1070 } else if (!ast_strlen_zero(applied->endpoint) && !applied->line) {
1071 ast_log(LOG_ERROR, "An endpoint has been specified on outbound registration '%s' without enabling line support\n",
1072 ast_sorcery_object_get_id(applied));
1076 if (state && can_reuse_registration(state->registration, applied)) {
1078 "No change between old configuration and new configuration on outbound registration '%s'. Using previous state\n",
1079 ast_sorcery_object_get_id(applied));
1080 ao2_replace(state->registration, applied);
1084 if (!(new_state = sip_outbound_registration_state_alloc(applied))) {
1088 if (ast_sip_push_task_synchronous(NULL, sip_outbound_registration_regc_alloc, new_state)) {
1092 if (ast_sip_push_task(new_state->client_state->serializer,
1093 sip_outbound_registration_perform, ao2_bump(new_state))) {
1094 ast_log(LOG_ERROR, "Failed to perform outbound registration on '%s'\n",
1095 ast_sorcery_object_get_id(new_state->registration));
1096 ao2_ref(new_state, -1);
1103 ao2_unlink(states, state);
1106 ao2_link(states, new_state);
1112 static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1114 struct sip_outbound_registration *registration = obj;
1116 return ast_sip_auth_vector_init(®istration->outbound_auths, var->value);
1119 static int outbound_auths_to_str(const void *obj, const intptr_t *args, char **buf)
1121 const struct sip_outbound_registration *registration = obj;
1123 return ast_sip_auths_to_str(®istration->outbound_auths, buf);
1126 static int outbound_auths_to_var_list(const void *obj, struct ast_variable **fields)
1128 const struct sip_outbound_registration *registration = obj;
1130 struct ast_variable *head = NULL;
1132 for (i = 0; i < AST_VECTOR_SIZE(®istration->outbound_auths) ; i++) {
1133 ast_variable_list_append(&head, ast_variable_new("outbound_auth",
1134 AST_VECTOR_GET(®istration->outbound_auths, i), ""));
1144 static int unregister_task(void *obj)
1146 RAII_VAR(struct sip_outbound_registration_state*, state, obj, ao2_cleanup);
1147 struct pjsip_regc *client = state->client_state->client;
1148 pjsip_tx_data *tdata;
1150 cancel_registration(state->client_state);
1152 if (pjsip_regc_unregister(client, &tdata) != PJ_SUCCESS) {
1156 ao2_ref(state->client_state, +1);
1157 if (pjsip_regc_send(client, tdata) != PJ_SUCCESS) {
1158 ao2_cleanup(state->client_state);
1164 static int queue_unregister(struct sip_outbound_registration_state *state)
1167 if (ast_sip_push_task(state->client_state->serializer, unregister_task, state)) {
1175 static int queue_register(struct sip_outbound_registration_state *state)
1178 if (ast_sip_push_task(state->client_state->serializer, sip_outbound_registration_perform, state)) {
1186 static char *cli_complete_registration(const char *line, const char *word,
1189 char *result = NULL;
1192 struct sip_outbound_registration *registration;
1193 RAII_VAR(struct ao2_container *, registrations, NULL, ao2_cleanup);
1194 struct ao2_iterator i;
1200 wordlen = strlen(word);
1201 registrations = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration",
1202 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
1203 if (!registrations) {
1207 i = ao2_iterator_init(registrations, 0);
1208 while ((registration = ao2_iterator_next(&i))) {
1209 const char *name = ast_sorcery_object_get_id(registration);
1210 if (!strncasecmp(word, name, wordlen) && ++which > state) {
1211 result = ast_strdup(name);
1214 ao2_cleanup(registration);
1219 ao2_iterator_destroy(&i);
1223 static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1225 RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
1226 const char *registration_name;
1230 e->command = "pjsip send unregister";
1232 "Usage: pjsip send unregister <registration>\n"
1233 " Unregisters the specified outbound registration and stops future registration attempts.\n";
1236 return cli_complete_registration(a->line, a->word, a->pos, a->n);
1240 return CLI_SHOWUSAGE;
1243 registration_name = a->argv[3];
1245 state = get_state(registration_name);
1247 ast_cli(a->fd, "Unable to retrieve registration %s\n", registration_name);
1251 if (queue_unregister(state)) {
1252 ast_cli(a->fd, "Failed to queue unregistration");
1259 static char *cli_register(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1261 RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
1262 const char *registration_name;
1266 e->command = "pjsip send register";
1268 "Usage: pjsip send register <registration>\n"
1269 " Unregisters the specified outbound "
1270 "registration then re-registers and re-schedules it.\n";
1273 return cli_complete_registration(a->line, a->word, a->pos, a->n);
1277 return CLI_SHOWUSAGE;
1280 registration_name = a->argv[3];
1282 state = get_state(registration_name);
1284 ast_cli(a->fd, "Unable to retrieve registration %s\n", registration_name);
1288 /* We need to serialize the unregister and register so they need
1289 * to be queued as separate tasks.
1291 if (queue_unregister(state)) {
1292 ast_cli(a->fd, "Failed to queue unregistration");
1295 if (queue_register(state)) {
1296 ast_cli(a->fd, "Failed to queue registration");
1303 static int ami_unregister(struct mansession *s, const struct message *m)
1305 const char *registration_name = astman_get_header(m, "Registration");
1306 RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
1308 if (ast_strlen_zero(registration_name)) {
1309 astman_send_error(s, m, "Registration parameter missing.");
1313 state = get_state(registration_name);
1315 astman_send_error(s, m, "Unable to retrieve registration entry\n");
1319 if (queue_unregister(state)) {
1320 astman_send_ack(s, m, "Failed to queue unregistration");
1324 astman_send_ack(s, m, "Unregistration sent");
1328 static int ami_register(struct mansession *s, const struct message *m)
1330 const char *registration_name = astman_get_header(m, "Registration");
1331 RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
1333 if (ast_strlen_zero(registration_name)) {
1334 astman_send_error(s, m, "Registration parameter missing.");
1338 state = get_state(registration_name);
1340 astman_send_error(s, m, "Unable to retrieve registration entry\n");
1344 /* We need to serialize the unregister and register so they need
1345 * to be queued as separate tasks.
1347 if (queue_unregister(state)) {
1348 astman_send_ack(s, m, "Failed to queue unregistration");
1351 if (queue_register(state)) {
1352 astman_send_ack(s, m, "Failed to queue unregistration");
1356 astman_send_ack(s, m, "Reregistration sent");
1360 struct sip_ami_outbound {
1361 struct ast_sip_ami *ami;
1364 struct sip_outbound_registration *registration;
1367 static int ami_outbound_registration_task(void *obj)
1369 struct sip_ami_outbound *ami = obj;
1370 RAII_VAR(struct ast_str *, buf, NULL, ast_free);
1371 struct sip_outbound_registration_state *state;
1373 buf = ast_sip_create_ami_event("OutboundRegistrationDetail", ami->ami);
1378 ast_sip_sorcery_object_to_ami(ami->registration, &buf);
1380 if ((state = get_state(ast_sorcery_object_get_id(ami->registration)))) {
1381 pjsip_regc_info info;
1383 if (state->client_state->status == SIP_REGISTRATION_REGISTERED) {
1386 ++ami->not_registered;
1389 ast_str_append(&buf, 0, "Status: %s\r\n",
1390 sip_outbound_registration_status_str[state->client_state->status]);
1392 pjsip_regc_get_info(state->client_state->client, &info);
1393 ast_str_append(&buf, 0, "NextReg: %d\r\n", info.next_reg);
1397 astman_append(ami->ami->s, "%s\r\n", ast_str_buffer(buf));
1398 return ast_sip_format_auths_ami(&ami->registration->outbound_auths, ami->ami);
1401 static int ami_outbound_registration_detail(void *obj, void *arg, int flags)
1403 struct sip_ami_outbound *ami = arg;
1405 ami->registration = obj;
1406 return ast_sip_push_task_synchronous(
1407 NULL, ami_outbound_registration_task, ami);
1410 static int ami_show_outbound_registrations(struct mansession *s,
1411 const struct message *m)
1413 struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), };
1414 struct sip_ami_outbound ami_outbound = { .ami = &ami };
1415 RAII_VAR(struct ao2_container *, regs, get_registrations(), ao2_cleanup);
1418 astman_send_error(s, m, "Unable to retreive "
1419 "outbound registrations\n");
1423 astman_send_listack(s, m, "Following are Events for each Outbound registration",
1426 ao2_callback(regs, OBJ_NODATA, ami_outbound_registration_detail, &ami_outbound);
1428 astman_send_list_complete_start(s, m, "OutboundRegistrationDetailComplete",
1429 ami_outbound.registered + ami_outbound.not_registered);
1431 "Registered: %d\r\n"
1432 "NotRegistered: %d\r\n",
1433 ami_outbound.registered,
1434 ami_outbound.not_registered);
1435 astman_send_list_complete_end(s);
1439 static struct ao2_container *cli_get_container(void)
1441 RAII_VAR(struct ao2_container *, container, get_registrations(), ao2_cleanup);
1442 struct ao2_container *s_container;
1448 s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
1449 ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
1454 if (ao2_container_dup(s_container, container, 0)) {
1455 ao2_ref(s_container, -1);
1462 static int cli_iterator(void *container, ao2_callback_fn callback, void *args)
1464 ao2_callback(container, OBJ_NODATA, callback, args);
1469 static void *cli_retrieve_by_id(const char *id)
1471 struct ao2_container *states;
1472 void *obj = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration", id);
1475 /* if the object no longer exists then remove its state */
1476 ao2_find((states = ao2_global_obj_ref(current_states)),
1477 id, OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA);
1478 ao2_ref(states, -1);
1484 static int cli_print_header(void *obj, void *arg, int flags)
1486 struct ast_sip_cli_context *context = arg;
1488 ast_assert(context->output_buffer != NULL);
1490 ast_str_append(&context->output_buffer, 0,
1491 " <Registration/ServerURI..............................> <Auth..........> <Status.......>\n");
1496 static int cli_print_body(void *obj, void *arg, int flags)
1498 struct sip_outbound_registration *registration = obj;
1499 struct ast_sip_cli_context *context = arg;
1500 const char *id = ast_sorcery_object_get_id(registration);
1501 struct sip_outbound_registration_state *state = get_state(id);
1502 #define REGISTRATION_URI_FIELD_LEN 53
1504 ast_assert(context->output_buffer != NULL);
1506 ast_str_append(&context->output_buffer, 0, " %-s/%-*.*s %-16s %-16s\n",
1508 (int) (REGISTRATION_URI_FIELD_LEN - strlen(id)),
1509 (int) (REGISTRATION_URI_FIELD_LEN - strlen(id)),
1510 registration->server_uri,
1511 AST_VECTOR_SIZE(®istration->outbound_auths)
1512 ? AST_VECTOR_GET(®istration->outbound_auths, 0)
1514 sip_outbound_registration_status_str[state->client_state->status]);
1517 if (context->show_details
1518 || (context->show_details_only_level_0 && context->indent_level == 0)) {
1519 ast_str_append(&context->output_buffer, 0, "\n");
1520 ast_sip_cli_print_sorcery_objectset(registration, context, 0);
1527 * A function pointer to callback needs to be within the
1528 * module in order to avoid problems with an undefined
1529 * symbol when the module is loaded.
1531 static char *my_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1533 return ast_sip_cli_traverse_objects(e, cmd, a);
1536 static struct ast_cli_entry cli_outbound_registration[] = {
1537 AST_CLI_DEFINE(cli_unregister, "Unregisters outbound registration target"),
1538 AST_CLI_DEFINE(cli_register, "Registers an outbound registration target"),
1539 AST_CLI_DEFINE(my_cli_traverse_objects, "List PJSIP Registrations",
1540 .command = "pjsip list registrations",
1541 .usage = "Usage: pjsip list registrations\n"
1542 " List the configured PJSIP Registrations\n"),
1543 AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Registrations",
1544 .command = "pjsip show registrations",
1545 .usage = "Usage: pjsip show registrations\n"
1546 " Show the configured PJSIP Registrations\n"),
1547 AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Registration",
1548 .command = "pjsip show registration",
1549 .usage = "Usage: pjsip show registration <id>\n"
1550 " Show the configured PJSIP Registration\n"),
1553 static struct ast_sip_cli_formatter_entry *cli_formatter;
1555 static void auth_observer(const char *type)
1557 struct sip_outbound_registration *registration;
1558 struct sip_outbound_registration_state *state;
1559 struct ao2_container *regs;
1560 const char *registration_id;
1561 struct ao2_iterator i;
1563 ast_debug(4, "Auths updated. Checking for any outbound registrations that are in permanent rejected state so they can be retried\n");
1565 regs = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration",
1566 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
1567 if (!regs || ao2_container_count(regs) == 0) {
1572 i = ao2_iterator_init(regs, 0);
1573 for (; (registration = ao2_iterator_next(&i)); ao2_ref(registration, -1)) {
1574 registration_id = ast_sorcery_object_get_id(registration);
1575 state = get_state(registration_id);
1576 if (state && state->client_state->status == SIP_REGISTRATION_REJECTED_PERMANENT) {
1577 ast_debug(4, "Trying outbound registration '%s' again\n", registration_id);
1579 if (ast_sip_push_task(state->client_state->serializer,
1580 sip_outbound_registration_perform, ao2_bump(state))) {
1581 ast_log(LOG_ERROR, "Failed to perform outbound registration on '%s'\n", registration_id);
1587 ao2_iterator_destroy(&i);
1591 const struct ast_sorcery_observer observer_callbacks = {
1592 .loaded = auth_observer,
1595 static int unload_module(void)
1597 ast_sip_unregister_endpoint_identifier(&line_identifier);
1598 ast_sorcery_observer_remove(ast_sip_get_sorcery(), "auth", &observer_callbacks);
1599 ast_cli_unregister_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
1600 ast_sip_unregister_cli_formatter(cli_formatter);
1601 ast_manager_unregister("PJSIPShowRegistrationsOutbound");
1602 ast_manager_unregister("PJSIPUnregister");
1603 ast_manager_unregister("PJSIPRegister");
1605 ao2_global_obj_release(current_states);
1610 static int load_module(void)
1612 struct ao2_container *registrations, *new_states;
1613 CHECK_PJSIP_MODULE_LOADED();
1615 ast_sorcery_apply_config(ast_sip_get_sorcery(), "res_pjsip_outbound_registration");
1616 ast_sorcery_apply_default(ast_sip_get_sorcery(), "registration", "config", "pjsip.conf,criteria=type=registration");
1618 if (ast_sorcery_object_register(ast_sip_get_sorcery(), "registration", sip_outbound_registration_alloc, NULL, sip_outbound_registration_apply)) {
1619 return AST_MODULE_LOAD_DECLINE;
1622 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "type", "", OPT_NOOP_T, 0, 0);
1623 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "server_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, server_uri));
1624 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "client_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, client_uri));
1625 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_user));
1626 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, transport));
1627 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, outbound_proxy));
1628 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, expiration));
1629 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_interval", "60", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_interval));
1630 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));
1631 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "max_retries", "10", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, max_retries));
1632 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));
1633 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);
1634 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, support_path));
1635 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "line", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, line));
1636 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, endpoint));
1637 ast_sip_register_endpoint_identifier(&line_identifier);
1639 ast_manager_register_xml("PJSIPUnregister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_unregister);
1640 ast_manager_register_xml("PJSIPRegister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_register);
1641 ast_manager_register_xml("PJSIPShowRegistrationsOutbound", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_show_outbound_registrations);
1643 cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
1644 if (!cli_formatter) {
1645 ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n");
1649 cli_formatter->name = "registration";
1650 cli_formatter->print_header = cli_print_header;
1651 cli_formatter->print_body = cli_print_body;
1652 cli_formatter->get_container = cli_get_container;
1653 cli_formatter->iterate = cli_iterator;
1654 cli_formatter->get_id = ast_sorcery_object_get_id;
1655 cli_formatter->retrieve_by_id = cli_retrieve_by_id;
1657 ast_sip_register_cli_formatter(cli_formatter);
1658 ast_cli_register_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
1660 if (!(new_states = ao2_container_alloc(
1661 DEFAULT_STATE_BUCKETS, registration_state_hash, registration_state_cmp))) {
1662 ast_log(LOG_ERROR, "Unable to allocate registration states container\n");
1664 return AST_MODULE_LOAD_FAILURE;
1666 ao2_global_obj_replace_unref(current_states, new_states);
1667 ao2_ref(new_states, -1);
1669 ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration");
1670 if (!(registrations = get_registrations())) {
1672 return AST_MODULE_LOAD_FAILURE;
1674 ao2_ref(registrations, -1);
1676 ast_sorcery_observer_add(ast_sip_get_sorcery(), "auth", &observer_callbacks);
1678 return AST_MODULE_LOAD_SUCCESS;
1681 static int reload_module(void)
1683 ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration");
1684 ao2_cleanup(get_registrations());
1688 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Outbound Registration Support",
1689 .support_level = AST_MODULE_SUPPORT_CORE,
1690 .load = load_module,
1691 .reload = reload_module,
1692 .unload = unload_module,
1693 .load_pri = AST_MODPRI_APP_DEPEND,