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="type">
118 <synopsis>Must be of type 'registration'.</synopsis>
120 <configOption name="support_path">
121 <synopsis>Enables Path support for outbound REGISTER requests.</synopsis>
123 When this option is enabled, outbound REGISTER requests will advertise
124 support for Path headers so that intervening proxies can add to the Path
126 </para></description>
131 <manager name="PJSIPUnregister" language="en_US">
133 Unregister an outbound registration.
136 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
137 <parameter name="Registration" required="true">
138 <para>The outbound registration to unregister.</para>
143 Send a SIP REGISTER request to the specified outbound registration with an expiration of 0.
144 This will cause the contact added by this registration to be removed on the remote system.
145 Note: The specified outbound registration will attempt to re-register according to it's last
146 registration expiration.
150 <manager name="PJSIPShowRegistrationsOutbound" language="en_US">
152 Lists PJSIP outbound registrations.
157 In response <literal>OutboundRegistrationDetail</literal> events showing configuration and status
158 information are raised for each outbound registration object. <literal>AuthDetail</literal>
159 events are raised for each associated auth object as well. Once all events are completed an
160 <literal>OutboundRegistrationDetailComplete</literal> is issued.
166 /*! \brief Amount of buffer time (in seconds) before expiration that we re-register at */
167 #define REREGISTER_BUFFER_TIME 10
169 /*! \brief Various states that an outbound registration may be in */
170 enum sip_outbound_registration_status {
171 /*! \brief Currently unregistered */
172 SIP_REGISTRATION_UNREGISTERED = 0,
173 /*! \brief Registered, yay! */
174 SIP_REGISTRATION_REGISTERED,
175 /*! \brief Registration was rejected, but response was temporal */
176 SIP_REGISTRATION_REJECTED_TEMPORARY,
177 /*! \brief Registration was rejected, permanently */
178 SIP_REGISTRATION_REJECTED_PERMANENT,
179 /*! \brief Registration has been stopped */
180 SIP_REGISTRATION_STOPPED,
183 static const char *sip_outbound_registration_status_str[] = {
184 [SIP_REGISTRATION_UNREGISTERED] = "Unregistered",
185 [SIP_REGISTRATION_REGISTERED] = "Registered",
186 [SIP_REGISTRATION_REJECTED_TEMPORARY] = "Rejected",
187 [SIP_REGISTRATION_REJECTED_PERMANENT] = "Rejected",
188 [SIP_REGISTRATION_STOPPED] = "Stopped",
191 /*! \brief Outbound registration client state information (persists for lifetime of regc) */
192 struct sip_outbound_registration_client_state {
193 /*! \brief Current status of this registration */
194 enum sip_outbound_registration_status status;
195 /*! \brief Outbound registration client */
197 /*! \brief Timer entry for retrying on temporal responses */
198 pj_timer_entry timer;
199 /*! \brief Current number of retries */
200 unsigned int retries;
201 /*! \brief Maximum number of retries permitted */
202 unsigned int max_retries;
203 /*! \brief Interval at which retries should occur for temporal responses */
204 unsigned int retry_interval;
205 /*! \brief Interval at which retries should occur for permanent responses */
206 unsigned int forbidden_retry_interval;
207 /*! \brief Treat authentication challenges that we cannot handle as permanent failures */
208 unsigned int auth_rejection_permanent;
209 /*! \brief Determines whether SIP Path support should be advertised */
210 unsigned int support_path;
211 /*! \brief Serializer for stuff and things */
212 struct ast_taskprocessor *serializer;
213 /*! \brief Configured authentication credentials */
214 struct ast_sip_auth_vector outbound_auths;
215 /*! \brief Registration should be destroyed after completion of transaction */
216 unsigned int destroy:1;
219 /*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
220 struct sip_outbound_registration_state {
221 /*! \brief Client state information */
222 struct sip_outbound_registration_client_state *client_state;
225 /*! \brief Outbound registration information */
226 struct sip_outbound_registration {
227 /*! \brief Sorcery object details */
228 SORCERY_OBJECT(details);
229 /*! \brief Stringfields */
230 AST_DECLARE_STRING_FIELDS(
231 /*! \brief URI for the registrar */
232 AST_STRING_FIELD(server_uri);
233 /*! \brief URI for the AOR */
234 AST_STRING_FIELD(client_uri);
235 /*! \brief Optional user for contact header */
236 AST_STRING_FIELD(contact_user);
237 /*! \brief Explicit transport to use for registration */
238 AST_STRING_FIELD(transport);
239 /*! \brief Outbound proxy to use */
240 AST_STRING_FIELD(outbound_proxy);
242 /*! \brief Requested expiration time */
243 unsigned int expiration;
244 /*! \brief Interval at which retries should occur for temporal responses */
245 unsigned int retry_interval;
246 /*! \brief Interval at which retries should occur for permanent responses */
247 unsigned int forbidden_retry_interval;
248 /*! \brief Treat authentication challenges that we cannot handle as permanent failures */
249 unsigned int auth_rejection_permanent;
250 /*! \brief Maximum number of retries permitted */
251 unsigned int max_retries;
252 /*! \brief Outbound registration state */
253 struct sip_outbound_registration_state *state;
254 /*! \brief Configured authentication credentials */
255 struct ast_sip_auth_vector outbound_auths;
256 /*! \brief Whether Path support is enabled */
257 unsigned int support_path;
260 /*! \brief Helper function which cancels the timer on a client */
261 static void cancel_registration(struct sip_outbound_registration_client_state *client_state)
263 if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &client_state->timer)) {
264 /* The timer was successfully cancelled, drop the refcount of client_state */
265 ao2_ref(client_state, -1);
269 static pj_str_t PATH_NAME = { "path", 4 };
271 /*! \brief Callback function for registering */
272 static int handle_client_registration(void *data)
274 RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
275 pjsip_tx_data *tdata;
276 pjsip_regc_info info;
277 char server_uri[PJSIP_MAX_URL_SIZE], client_uri[PJSIP_MAX_URL_SIZE];
279 cancel_registration(client_state);
281 if ((client_state->status == SIP_REGISTRATION_STOPPED) ||
282 (pjsip_regc_register(client_state->client, PJ_FALSE, &tdata) != PJ_SUCCESS)) {
286 pjsip_regc_get_info(client_state->client, &info);
287 ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
288 ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
289 ast_debug(3, "REGISTER attempt %u to '%s' with client '%s'\n",
290 client_state->retries + 1, server_uri, client_uri);
292 if (client_state->support_path) {
293 pjsip_supported_hdr *hdr;
295 hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL);
297 /* insert a new Supported header */
298 hdr = pjsip_supported_hdr_create(tdata->pool);
303 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
306 /* add on to the existing Supported header */
307 pj_strassign(&hdr->values[hdr->count++], &PATH_NAME);
310 /* Due to the registration the callback may now get called, so bump the ref count */
311 ao2_ref(client_state, +1);
312 if (pjsip_regc_send(client_state->client, tdata) != PJ_SUCCESS) {
313 ao2_ref(client_state, -1);
319 /*! \brief Timer callback function, used just for registrations */
320 static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
322 struct sip_outbound_registration_client_state *client_state = entry->user_data;
324 ao2_ref(client_state, +1);
325 if (ast_sip_push_task(client_state->serializer, handle_client_registration, client_state)) {
326 ast_log(LOG_WARNING, "Failed to pass outbound registration to threadpool\n");
327 ao2_ref(client_state, -1);
333 /*! \brief Helper function which sets up the timer to re-register in a specific amount of time */
334 static void schedule_registration(struct sip_outbound_registration_client_state *client_state, unsigned int seconds)
336 pj_time_val delay = { .sec = seconds, };
338 cancel_registration(client_state);
340 ao2_ref(client_state, +1);
341 if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &client_state->timer, &delay) != PJ_SUCCESS) {
342 ast_log(LOG_WARNING, "Failed to pass timed registration to scheduler\n");
343 ao2_ref(client_state, -1);
347 /*! \brief Callback function for unregistering (potentially) and destroying state */
348 static int handle_client_state_destruction(void *data)
350 RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
352 cancel_registration(client_state);
354 if (client_state->client) {
355 pjsip_regc_info info;
357 pjsip_regc_get_info(client_state->client, &info);
359 if (info.is_busy == PJ_TRUE) {
360 /* If a client transaction is in progress we defer until it is complete */
361 client_state->destroy = 1;
365 if (client_state->status != SIP_REGISTRATION_UNREGISTERED && client_state->status != SIP_REGISTRATION_REJECTED_PERMANENT) {
366 pjsip_tx_data *tdata;
368 if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS) {
369 pjsip_regc_send(client_state->client, tdata);
373 pjsip_regc_destroy(client_state->client);
376 client_state->status = SIP_REGISTRATION_STOPPED;
377 ast_sip_auth_vector_destroy(&client_state->outbound_auths);
382 /*! \brief Structure for registration response */
383 struct registration_response {
384 /*! \brief Response code for the registration attempt */
386 /*! \brief Expiration time for registration */
388 /*! \brief Retry-After value */
390 /*! \brief Outbound registration client state */
391 struct sip_outbound_registration_client_state *client_state;
392 /*! \brief The response message */
393 pjsip_rx_data *rdata;
394 /*! \brief The response transaction */
395 pjsip_transaction *tsx;
398 /*! \brief Registration response structure destructor */
399 static void registration_response_destroy(void *obj)
401 struct registration_response *response = obj;
403 if (response->rdata) {
404 pjsip_rx_data_free_cloned(response->rdata);
407 ao2_cleanup(response->client_state);
410 /* \brief Helper funtion which determines if a response code is temporal or not */
411 static int sip_outbound_registration_is_temporal(unsigned int code,
412 struct sip_outbound_registration_client_state *client_state)
414 /* Shamelessly taken from pjsua */
415 if (code == PJSIP_SC_REQUEST_TIMEOUT ||
416 code == PJSIP_SC_INTERNAL_SERVER_ERROR ||
417 code == PJSIP_SC_BAD_GATEWAY ||
418 code == PJSIP_SC_SERVICE_UNAVAILABLE ||
419 code == PJSIP_SC_SERVER_TIMEOUT ||
420 ((code == PJSIP_SC_UNAUTHORIZED ||
421 code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED) &&
422 !client_state->auth_rejection_permanent) ||
423 PJSIP_IS_STATUS_IN_CLASS(code, 600)) {
430 static void schedule_retry(struct registration_response *response, unsigned int interval,
431 const char *server_uri, const char *client_uri)
433 response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
434 schedule_registration(response->client_state, interval);
436 if (response->rdata) {
437 ast_log(LOG_WARNING, "Temporal response '%d' received from '%s' on "
438 "registration attempt to '%s', retrying in '%u'\n",
439 response->code, server_uri, client_uri, interval);
441 ast_log(LOG_WARNING, "No response received from '%s' on "
442 "registration attempt to '%s', retrying in '%u'\n",
443 server_uri, client_uri, interval);
447 /*! \brief Callback function for handling a response to a registration attempt */
448 static int handle_registration_response(void *data)
450 RAII_VAR(struct registration_response *, response, data, ao2_cleanup);
451 pjsip_regc_info info;
452 char server_uri[PJSIP_MAX_URL_SIZE], client_uri[PJSIP_MAX_URL_SIZE];
454 if (response->client_state->status == SIP_REGISTRATION_STOPPED) {
458 pjsip_regc_get_info(response->client_state->client, &info);
459 ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
460 ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
462 if (response->code == 401 || response->code == 407) {
463 pjsip_tx_data *tdata;
464 if (!ast_sip_create_request_with_auth(&response->client_state->outbound_auths,
465 response->rdata, response->tsx, &tdata)) {
466 ao2_ref(response->client_state, +1);
467 if (pjsip_regc_send(response->client_state->client, tdata) != PJ_SUCCESS) {
468 ao2_cleanup(response->client_state);
472 /* Otherwise, fall through so the failure is processed appropriately */
475 if (PJSIP_IS_STATUS_IN_CLASS(response->code, 200)) {
476 /* Check if this is in regards to registering or unregistering */
477 if (response->expiration) {
478 /* If the registration went fine simply reschedule registration for the future */
479 ast_debug(1, "Outbound registration to '%s' with client '%s' successful\n", server_uri, client_uri);
480 response->client_state->status = SIP_REGISTRATION_REGISTERED;
481 response->client_state->retries = 0;
482 schedule_registration(response->client_state, response->expiration - REREGISTER_BUFFER_TIME);
484 ast_debug(1, "Outbound unregistration to '%s' with client '%s' successful\n", server_uri, client_uri);
485 response->client_state->status = SIP_REGISTRATION_UNREGISTERED;
487 } else if (response->retry_after) {
488 /* If we have been instructed to retry after a period of time, schedule it as such */
489 schedule_retry(response, response->retry_after, server_uri, client_uri);
490 } else if (response->client_state->retry_interval && sip_outbound_registration_is_temporal(response->code, response->client_state)) {
491 if (response->client_state->retries == response->client_state->max_retries) {
492 /* If we received enough temporal responses to exceed our maximum give up permanently */
493 response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
494 ast_log(LOG_WARNING, "Maximum retries reached when attempting outbound registration to '%s' with client '%s', stopping registration attempt\n",
495 server_uri, client_uri);
497 /* On the other hand if we can still try some more do so */
498 response->client_state->retries++;
499 schedule_retry(response, response->client_state->retry_interval, server_uri, client_uri);
502 if (response->code == 403
503 && response->client_state->forbidden_retry_interval
504 && response->client_state->retries < response->client_state->max_retries) {
505 /* A forbidden response retry interval is configured and there are retries remaining */
506 response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
507 response->client_state->retries++;
508 schedule_registration(response->client_state, response->client_state->forbidden_retry_interval);
509 ast_log(LOG_WARNING, "403 Forbidden fatal response received from '%s' on registration attempt to '%s', retrying in '%u' seconds\n",
510 server_uri, client_uri, response->client_state->forbidden_retry_interval);
512 /* Finally if there's no hope of registering give up */
513 response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
514 if (response->rdata) {
515 ast_log(LOG_WARNING, "Fatal response '%d' received from '%s' on registration attempt to '%s', stopping outbound registration\n",
516 response->code, server_uri, client_uri);
518 ast_log(LOG_WARNING, "Fatal registration attempt to '%s', stopping outbound registration\n", client_uri);
523 ast_system_publish_registry("PJSIP", client_uri, server_uri, sip_outbound_registration_status_str[response->client_state->status], NULL);
525 /* If deferred destruction is in use see if we need to destroy now */
526 if (response->client_state->destroy) {
527 handle_client_state_destruction(response->client_state);
533 /*! \brief Callback function for outbound registration client */
534 static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *param)
536 RAII_VAR(struct sip_outbound_registration_client_state *, client_state, param->token, ao2_cleanup);
537 struct registration_response *response = ao2_alloc(sizeof(*response), registration_response_destroy);
539 response->code = param->code;
540 response->expiration = param->expiration;
541 response->client_state = client_state;
542 ao2_ref(response->client_state, +1);
545 struct pjsip_retry_after_hdr *retry_after = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_RETRY_AFTER, NULL);
547 response->retry_after = retry_after ? retry_after->ivalue : 0;
548 response->tsx = pjsip_rdata_get_tsx(param->rdata);
549 pjsip_rx_data_clone(param->rdata, 0, &response->rdata);
552 if (ast_sip_push_task(client_state->serializer, handle_registration_response, response)) {
553 ast_log(LOG_WARNING, "Failed to pass incoming registration response to threadpool\n");
554 ao2_cleanup(response);
558 /*! \brief Destructor function for registration state */
559 static void sip_outbound_registration_state_destroy(void *obj)
561 struct sip_outbound_registration_state *state = obj;
563 if (!state->client_state) {
567 if (state->client_state->serializer && ast_sip_push_task(state->client_state->serializer, handle_client_state_destruction, state->client_state)) {
568 ast_log(LOG_WARNING, "Failed to pass outbound registration client destruction to threadpool\n");
569 ao2_ref(state->client_state, -1);
573 /*! \brief Destructor function for client registration state */
574 static void sip_outbound_registration_client_state_destroy(void *obj)
576 struct sip_outbound_registration_client_state *client_state = obj;
578 ast_taskprocessor_unreference(client_state->serializer);
581 /*! \brief Allocator function for registration state */
582 static struct sip_outbound_registration_state *sip_outbound_registration_state_alloc(void)
584 struct sip_outbound_registration_state *state = ao2_alloc(sizeof(*state), sip_outbound_registration_state_destroy);
586 if (!state || !(state->client_state = ao2_alloc(sizeof(*state->client_state), sip_outbound_registration_client_state_destroy))) {
591 if (!(state->client_state->serializer = ast_sip_create_serializer())) {
592 ao2_cleanup(state->client_state);
597 state->client_state->status = SIP_REGISTRATION_UNREGISTERED;
598 state->client_state->timer.user_data = state->client_state;
599 state->client_state->timer.cb = sip_outbound_registration_timer_cb;
604 /*! \brief Destructor function for registration information */
605 static void sip_outbound_registration_destroy(void *obj)
607 struct sip_outbound_registration *registration = obj;
609 ao2_cleanup(registration->state);
610 ast_sip_auth_vector_destroy(®istration->outbound_auths);
612 ast_string_field_free_memory(registration);
615 /*! \brief Allocator function for registration information */
616 static void *sip_outbound_registration_alloc(const char *name)
618 struct sip_outbound_registration *registration = ast_sorcery_generic_alloc(sizeof(*registration), sip_outbound_registration_destroy);
620 if (!registration || ast_string_field_init(registration, 256)) {
621 ao2_cleanup(registration);
628 /*! \brief Helper function which populates a pj_str_t with a contact header */
629 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)
631 pj_str_t tmp, local_addr;
633 pjsip_sip_uri *sip_uri;
634 pjsip_transport_type_e type = PJSIP_TRANSPORT_UNSPECIFIED;
637 pj_strdup_with_null(pool, &tmp, target);
639 if (!(uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0)) ||
640 (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))) {
644 sip_uri = pjsip_uri_get_uri(uri);
646 if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri)) {
647 type = PJSIP_TRANSPORT_TLS;
648 } else if (!sip_uri->transport_param.slen) {
649 type = PJSIP_TRANSPORT_UDP;
651 type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
654 if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
658 if (pj_strchr(&sip_uri->host, ':')) {
659 type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
662 if (pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), pool, type, selector,
663 &local_addr, &local_port) != PJ_SUCCESS) {
667 if (!pj_strchr(&sip_uri->host, ':') && pj_strchr(&local_addr, ':')) {
668 type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
671 contact->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
672 contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
673 "<%s:%s@%s%.*s%s:%d%s%s>",
674 (pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE) ? "sips" : "sip",
676 (type & PJSIP_TRANSPORT_IPV6) ? "[" : "",
677 (int)local_addr.slen,
679 (type & PJSIP_TRANSPORT_IPV6) ? "]" : "",
681 (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? ";transport=" : "",
682 (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? pjsip_transport_get_type_name(type) : "");
689 * \brief Check if a registration can be reused
691 * This checks if the existing outbound registration's configuration differs from a newly-applied
692 * outbound registration to see if the applied one.
694 * \param existing The pre-existing outbound registration
695 * \param applied The newly-created registration
697 static int can_reuse_registration(struct sip_outbound_registration *existing, struct sip_outbound_registration *applied)
701 if (strcmp(existing->server_uri, applied->server_uri) || strcmp(existing->client_uri, applied->client_uri) ||
702 strcmp(existing->transport, applied->transport) || strcmp(existing->contact_user, applied->contact_user) ||
703 strcmp(existing->outbound_proxy, applied->outbound_proxy) ||
704 AST_VECTOR_SIZE(&existing->outbound_auths) != AST_VECTOR_SIZE(&applied->outbound_auths) ||
705 existing->auth_rejection_permanent != applied->auth_rejection_permanent) {
709 for (i = 0; i < AST_VECTOR_SIZE(&existing->outbound_auths); ++i) {
710 if (strcmp(AST_VECTOR_GET(&existing->outbound_auths, i), AST_VECTOR_GET(&applied->outbound_auths, i))) {
718 /*! \brief Helper function that allocates a pjsip registration client and configures it */
719 static int sip_outbound_registration_regc_alloc(void *data)
721 struct sip_outbound_registration *registration = data;
725 pj_str_t server_uri, client_uri, contact_uri;
726 pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
728 pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "URI Validation", 256, 256);
730 ast_log(LOG_ERROR, "Could not create pool for URI validation on outbound registration '%s'\n",
731 ast_sorcery_object_get_id(registration));
735 pj_strdup2_with_null(pool, &tmp, registration->server_uri);
736 uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
738 ast_log(LOG_ERROR, "Invalid server URI '%s' specified on outbound registration '%s'\n",
739 registration->server_uri, ast_sorcery_object_get_id(registration));
740 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
744 pj_strdup2_with_null(pool, &tmp, registration->client_uri);
745 uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
747 ast_log(LOG_ERROR, "Invalid client URI '%s' specified on outbound registration '%s'\n",
748 registration->client_uri, ast_sorcery_object_get_id(registration));
749 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
753 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
755 if (!ast_strlen_zero(registration->transport)) {
756 RAII_VAR(struct ast_sip_transport *, transport, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", registration->transport), ao2_cleanup);
758 if (!transport || !transport->state) {
759 ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport '%s' "
760 " for outbound registration", registration->transport);
764 if (transport->state->transport) {
765 selector.type = PJSIP_TPSELECTOR_TRANSPORT;
766 selector.u.transport = transport->state->transport;
767 } else if (transport->state->factory) {
768 selector.type = PJSIP_TPSELECTOR_LISTENER;
769 selector.u.listener = transport->state->factory;
775 if (!registration->state->client_state->client &&
776 pjsip_regc_create(ast_sip_get_pjsip_endpoint(), registration->state->client_state, sip_outbound_registration_response_cb,
777 ®istration->state->client_state->client) != PJ_SUCCESS) {
781 pjsip_regc_set_transport(registration->state->client_state->client, &selector);
783 if (!ast_strlen_zero(registration->outbound_proxy)) {
784 pjsip_route_hdr route_set, *route;
785 static const pj_str_t ROUTE_HNAME = { "Route", 5 };
788 pj_list_init(&route_set);
790 pj_strdup2_with_null(pjsip_regc_get_pool(registration->state->client_state->client), &tmp, registration->outbound_proxy);
791 if (!(route = pjsip_parse_hdr(pjsip_regc_get_pool(registration->state->client_state->client), &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL))) {
794 pj_list_insert_nodes_before(&route_set, route);
796 pjsip_regc_set_route_set(registration->state->client_state->client, &route_set);
799 pj_cstr(&server_uri, registration->server_uri);
801 if (sip_dialog_create_contact(pjsip_regc_get_pool(registration->state->client_state->client), &contact_uri, S_OR(registration->contact_user, "s"), &server_uri, &selector)) {
805 pj_cstr(&client_uri, registration->client_uri);
807 if (pjsip_regc_init(registration->state->client_state->client, &server_uri, &client_uri, &client_uri, 1, &contact_uri, registration->expiration) != PJ_SUCCESS) {
814 /*! \brief Apply function which finds or allocates a state structure */
815 static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, void *obj)
817 RAII_VAR(struct sip_outbound_registration *, existing, ast_sorcery_retrieve_by_id(sorcery, "registration", ast_sorcery_object_get_id(obj)), ao2_cleanup);
818 struct sip_outbound_registration *applied = obj;
820 if (ast_strlen_zero(applied->server_uri)) {
821 ast_log(LOG_ERROR, "No server URI specified on outbound registration '%s'",
822 ast_sorcery_object_get_id(applied));
824 } else if (ast_strlen_zero(applied->client_uri)) {
825 ast_log(LOG_ERROR, "No client URI specified on outbound registration '%s'\n",
826 ast_sorcery_object_get_id(applied));
831 /* If no existing registration exists we can just start fresh easily */
832 applied->state = sip_outbound_registration_state_alloc();
834 /* If there is an existing registration things are more complicated, we can immediately reuse this state if most stuff remains unchanged */
835 if (can_reuse_registration(existing, applied)) {
836 applied->state = existing->state;
837 ao2_ref(applied->state, +1);
840 applied->state = sip_outbound_registration_state_alloc();
843 if (!applied->state) {
847 return ast_sip_push_task_synchronous(NULL, sip_outbound_registration_regc_alloc, applied);
850 /*! \brief Helper function which performs a single registration */
851 static int sip_outbound_registration_perform(void *data)
853 RAII_VAR(struct sip_outbound_registration *, registration, data, ao2_cleanup);
856 /* Just in case the client state is being reused for this registration, free the auth information */
857 ast_sip_auth_vector_destroy(®istration->state->client_state->outbound_auths);
859 AST_VECTOR_INIT(®istration->state->client_state->outbound_auths, AST_VECTOR_SIZE(®istration->outbound_auths));
860 for (i = 0; i < AST_VECTOR_SIZE(®istration->outbound_auths); ++i) {
861 const char *name = ast_strdup(AST_VECTOR_GET(®istration->outbound_auths, i));
862 AST_VECTOR_APPEND(®istration->state->client_state->outbound_auths, name);
864 registration->state->client_state->retry_interval = registration->retry_interval;
865 registration->state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval;
866 registration->state->client_state->max_retries = registration->max_retries;
867 registration->state->client_state->retries = 0;
868 registration->state->client_state->support_path = registration->support_path;
869 registration->state->client_state->auth_rejection_permanent = registration->auth_rejection_permanent;
871 pjsip_regc_update_expires(registration->state->client_state->client, registration->expiration);
873 schedule_registration(registration->state->client_state, (ast_random() % 10) + 1);
878 /*! \brief Helper function which performs all registrations */
879 static void sip_outbound_registration_perform_all(void)
881 RAII_VAR(struct ao2_container *, registrations, ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL), ao2_cleanup);
882 struct ao2_iterator i;
883 struct sip_outbound_registration *registration;
885 if (!registrations) {
889 i = ao2_iterator_init(registrations, 0);
890 while ((registration = ao2_iterator_next(&i))) {
891 if (ast_sip_push_task(registration->state->client_state->serializer, sip_outbound_registration_perform, registration)) {
892 ast_log(LOG_ERROR, "Failed to perform outbound registration on '%s'\n", ast_sorcery_object_get_id(registration));
893 ao2_ref(registration, -1);
896 ao2_iterator_destroy(&i);
899 static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
901 struct sip_outbound_registration *registration = obj;
903 return ast_sip_auth_vector_init(®istration->outbound_auths, var->value);
906 static int outbound_auths_to_str(const void *obj, const intptr_t *args, char **buf)
908 const struct sip_outbound_registration *registration = obj;
910 return ast_sip_auths_to_str(®istration->outbound_auths, buf);
913 static int outbound_auths_to_var_list(const void *obj, struct ast_variable **fields)
915 const struct sip_outbound_registration *registration = obj;
917 struct ast_variable *head = NULL;
919 for (i = 0; i < AST_VECTOR_SIZE(®istration->outbound_auths) ; i++) {
920 ast_variable_list_append(&head, ast_variable_new("outbound_auth",
921 AST_VECTOR_GET(®istration->outbound_auths, i), ""));
931 static struct sip_outbound_registration *retrieve_registration(const char *registration_name)
933 return ast_sorcery_retrieve_by_id(
934 ast_sip_get_sorcery(),
939 static int unregister_task(void *obj)
941 RAII_VAR(struct sip_outbound_registration*, registration, obj, ao2_cleanup);
942 struct pjsip_regc *client = registration->state->client_state->client;
943 pjsip_tx_data *tdata;
945 if (pjsip_regc_unregister(client, &tdata) != PJ_SUCCESS) {
949 ao2_ref(registration->state->client_state, +1);
950 if (pjsip_regc_send(client, tdata) != PJ_SUCCESS) {
951 ao2_cleanup(registration->state->client_state);
957 static int queue_unregister(struct sip_outbound_registration *registration)
959 ao2_ref(registration, +1);
960 if (ast_sip_push_task(registration->state->client_state->serializer, unregister_task, registration)) {
961 ao2_cleanup(registration);
967 static char *cli_complete_registration(const char *line, const char *word,
973 struct sip_outbound_registration *registration;
974 RAII_VAR(struct ao2_container *, registrations, NULL, ao2_cleanup);
975 struct ao2_iterator i;
981 wordlen = strlen(word);
982 registrations = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration",
983 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
984 if (!registrations) {
988 i = ao2_iterator_init(registrations, 0);
989 while ((registration = ao2_iterator_next(&i))) {
990 const char *name = ast_sorcery_object_get_id(registration);
991 if (!strncasecmp(word, name, wordlen) && ++which > state) {
992 result = ast_strdup(name);
995 ao2_cleanup(registration);
1000 ao2_iterator_destroy(&i);
1004 static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1006 RAII_VAR(struct sip_outbound_registration *, registration, NULL, ao2_cleanup);
1007 const char *registration_name;
1011 e->command = "pjsip send unregister";
1013 "Usage: pjsip send unregister <registration>\n"
1014 " Send a SIP REGISTER request to the specified outbound "
1015 "registration with an expiration of 0. This will cause the contact "
1016 "added by this registration to be removed on the remote system. Note: "
1017 "The specified outbound registration will attempt to re-register "
1018 "according to its last registration expiration.\n";
1021 return cli_complete_registration(a->line, a->word, a->pos, a->n);
1025 return CLI_SHOWUSAGE;
1028 registration_name = a->argv[3];
1030 registration = retrieve_registration(registration_name);
1031 if (!registration) {
1032 ast_cli(a->fd, "Unable to retrieve registration %s\n", registration_name);
1036 if (queue_unregister(registration)) {
1037 ast_cli(a->fd, "Failed to queue unregistration");
1044 static int ami_unregister(struct mansession *s, const struct message *m)
1046 const char *registration_name = astman_get_header(m, "Registration");
1047 RAII_VAR(struct sip_outbound_registration *, registration, NULL, ao2_cleanup);
1049 if (ast_strlen_zero(registration_name)) {
1050 astman_send_error(s, m, "Registration parameter missing.");
1054 registration = retrieve_registration(registration_name);
1055 if (!registration) {
1056 astman_send_error(s, m, "Unable to retrieve registration entry\n");
1061 if (queue_unregister(registration)) {
1062 astman_send_ack(s, m, "Failed to queue unregistration");
1066 astman_send_ack(s, m, "Unregistration sent");
1070 struct sip_ami_outbound {
1071 struct ast_sip_ami *ami;
1074 struct sip_outbound_registration *registration;
1077 static int ami_outbound_registration_task(void *obj)
1079 struct sip_ami_outbound *ami = obj;
1080 RAII_VAR(struct ast_str *, buf,
1081 ast_sip_create_ami_event("OutboundRegistrationDetail", ami->ami), ast_free);
1087 ast_sip_sorcery_object_to_ami(ami->registration, &buf);
1089 if (ami->registration->state) {
1090 pjsip_regc_info info;
1091 if (ami->registration->state->client_state->status ==
1092 SIP_REGISTRATION_REGISTERED) {
1095 ++ami->not_registered;
1098 ast_str_append(&buf, 0, "Status: %s%s",
1099 sip_outbound_registration_status_str[
1100 ami->registration->state->client_state->status], "\r\n");
1102 pjsip_regc_get_info(ami->registration->state->client_state->client, &info);
1103 ast_str_append(&buf, 0, "NextReg: %d%s", info.next_reg, "\r\n");
1106 astman_append(ami->ami->s, "%s\r\n", ast_str_buffer(buf));
1107 return ast_sip_format_auths_ami(&ami->registration->outbound_auths, ami->ami);
1110 static int ami_outbound_registration_detail(void *obj, void *arg, int flags)
1112 struct sip_ami_outbound *ami = arg;
1114 ami->registration = obj;
1115 return ast_sip_push_task_synchronous(
1116 NULL, ami_outbound_registration_task, ami);
1119 static int ami_show_outbound_registrations(struct mansession *s,
1120 const struct message *m)
1122 struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), };
1123 struct sip_ami_outbound ami_outbound = { .ami = &ami };
1124 RAII_VAR(struct ao2_container *, regs, ast_sorcery_retrieve_by_fields(
1125 ast_sip_get_sorcery(), "registration", AST_RETRIEVE_FLAG_MULTIPLE |
1126 AST_RETRIEVE_FLAG_ALL, NULL), ao2_cleanup);
1129 astman_send_error(s, m, "Unable to retreive "
1130 "outbound registrations\n");
1134 astman_send_listack(s, m, "Following are Events for each Outbound "
1135 "registration", "start");
1137 ao2_callback(regs, OBJ_NODATA, ami_outbound_registration_detail, &ami_outbound);
1139 astman_append(s, "Event: OutboundRegistrationDetailComplete\r\n");
1140 if (!ast_strlen_zero(ami.action_id)) {
1141 astman_append(s, "ActionID: %s\r\n", ami.action_id);
1143 astman_append(s, "EventList: Complete\r\n"
1144 "Registered: %d\r\n"
1145 "NotRegistered: %d\r\n\r\n",
1146 ami_outbound.registered,
1147 ami_outbound.not_registered);
1151 static struct ao2_container *cli_get_container(void)
1153 RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
1154 struct ao2_container *s_container;
1156 container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration",
1157 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
1162 s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
1163 ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
1168 if (ao2_container_dup(s_container, container, 0)) {
1169 ao2_ref(s_container, -1);
1176 static int cli_iterator(void *container, ao2_callback_fn callback, void *args)
1178 ao2_callback(container, OBJ_NODATA, callback, args);
1183 static void *cli_retrieve_by_id(const char *id)
1185 return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration", id);
1188 static int cli_print_header(void *obj, void *arg, int flags)
1190 struct ast_sip_cli_context *context = arg;
1192 ast_assert(context->output_buffer != NULL);
1194 ast_str_append(&context->output_buffer, 0,
1195 " <Registration/ServerURI..............................> <Auth..........> <Status.......>\n");
1200 static int cli_print_body(void *obj, void *arg, int flags)
1202 struct sip_outbound_registration *registration = obj;
1203 struct ast_sip_cli_context *context = arg;
1204 const char *id = ast_sorcery_object_get_id(registration);
1205 #define REGISTRATION_URI_FIELD_LEN 53
1207 ast_assert(context->output_buffer != NULL);
1209 ast_str_append(&context->output_buffer, 0, " %-s/%-*.*s %-16s %-16s\n",
1211 (int) (REGISTRATION_URI_FIELD_LEN - strlen(id)),
1212 (int) (REGISTRATION_URI_FIELD_LEN - strlen(id)),
1213 registration->server_uri,
1214 AST_VECTOR_SIZE(®istration->outbound_auths)
1215 ? AST_VECTOR_GET(®istration->outbound_auths, 0)
1217 sip_outbound_registration_status_str[registration->state->client_state->status]);
1219 if (context->show_details
1220 || (context->show_details_only_level_0 && context->indent_level == 0)) {
1221 ast_str_append(&context->output_buffer, 0, "\n");
1222 ast_sip_cli_print_sorcery_objectset(registration, context, 0);
1229 * A function pointer to callback needs to be within the
1230 * module in order to avoid problems with an undefined
1231 * symbol when the module is loaded.
1233 static char *my_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1235 return ast_sip_cli_traverse_objects(e, cmd, a);
1238 static struct ast_cli_entry cli_outbound_registration[] = {
1239 AST_CLI_DEFINE(cli_unregister, "Send a REGISTER request to an outbound registration target with a expiration of 0"),
1240 AST_CLI_DEFINE(my_cli_traverse_objects, "List PJSIP Registrations",
1241 .command = "pjsip list registrations",
1242 .usage = "Usage: pjsip list registrations\n"
1243 " List the configured PJSIP Registrations\n"),
1244 AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Registrations",
1245 .command = "pjsip show registrations",
1246 .usage = "Usage: pjsip show registrations\n"
1247 " Show the configured PJSIP Registrations\n"),
1248 AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Registration",
1249 .command = "pjsip show registration",
1250 .usage = "Usage: pjsip show registration <id>\n"
1251 " Show the configured PJSIP Registration\n"),
1254 static struct ast_sip_cli_formatter_entry *cli_formatter;
1256 static int load_module(void)
1258 CHECK_PJSIP_MODULE_LOADED();
1260 ast_sorcery_apply_default(ast_sip_get_sorcery(), "registration", "config", "pjsip.conf,criteria=type=registration");
1262 if (ast_sorcery_object_register(ast_sip_get_sorcery(), "registration", sip_outbound_registration_alloc, NULL, sip_outbound_registration_apply)) {
1263 return AST_MODULE_LOAD_DECLINE;
1266 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "type", "", OPT_NOOP_T, 0, 0);
1267 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "server_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, server_uri));
1268 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "client_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, client_uri));
1269 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_user));
1270 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, transport));
1271 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, outbound_proxy));
1272 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, expiration));
1273 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_interval", "60", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_interval));
1274 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));
1275 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "max_retries", "10", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, max_retries));
1276 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));
1277 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);
1278 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, support_path));
1279 ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration");
1280 sip_outbound_registration_perform_all();
1282 ast_manager_register_xml("PJSIPUnregister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_unregister);
1283 ast_manager_register_xml("PJSIPShowRegistrationsOutbound", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_show_outbound_registrations);
1285 cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
1286 if (!cli_formatter) {
1287 ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n");
1290 cli_formatter->name = "registration";
1291 cli_formatter->print_header = cli_print_header;
1292 cli_formatter->print_body = cli_print_body;
1293 cli_formatter->get_container = cli_get_container;
1294 cli_formatter->iterate = cli_iterator;
1295 cli_formatter->get_id = ast_sorcery_object_get_id;
1296 cli_formatter->retrieve_by_id = cli_retrieve_by_id;
1298 ast_sip_register_cli_formatter(cli_formatter);
1299 ast_cli_register_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
1301 return AST_MODULE_LOAD_SUCCESS;
1304 static int reload_module(void)
1306 ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration");
1307 sip_outbound_registration_perform_all();
1311 static int unload_module(void)
1313 ast_cli_unregister_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
1314 ast_sip_unregister_cli_formatter(cli_formatter);
1315 ast_manager_unregister("PJSIPShowRegistrationsOutbound");
1316 ast_manager_unregister("PJSIPUnregister");
1321 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Outbound Registration Support",
1322 .support_level = AST_MODULE_SUPPORT_CORE,
1323 .load = load_module,
1324 .reload = reload_module,
1325 .unload = unload_module,
1326 .load_pri = AST_MODPRI_APP_DEPEND,