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>
142 <manager name="PJSIPShowRegistrationsOutbound" language="en_US">
144 Lists PJSIP outbound registrations.
149 In response <literal>OutboundRegistrationDetail</literal> events showing configuration and status
150 information are raised for each outbound registration object. <literal>AuthDetail</literal>
151 events are raised for each associated auth object as well. Once all events are completed an
152 <literal>OutboundRegistrationDetailComplete</literal> is issued.
158 /*! \brief Amount of buffer time (in seconds) before expiration that we re-register at */
159 #define REREGISTER_BUFFER_TIME 10
161 /*! \brief Various states that an outbound registration may be in */
162 enum sip_outbound_registration_status {
163 /*! \brief Currently unregistered */
164 SIP_REGISTRATION_UNREGISTERED = 0,
165 /*! \brief Registered, yay! */
166 SIP_REGISTRATION_REGISTERED,
167 /*! \brief Registration was rejected, but response was temporal */
168 SIP_REGISTRATION_REJECTED_TEMPORARY,
169 /*! \brief Registration was rejected, permanently */
170 SIP_REGISTRATION_REJECTED_PERMANENT,
171 /*! \brief Registration has been stopped */
172 SIP_REGISTRATION_STOPPED,
175 static const char *sip_outbound_registration_status_str[] = {
176 [SIP_REGISTRATION_UNREGISTERED] = "Unregistered",
177 [SIP_REGISTRATION_REGISTERED] = "Registered",
178 [SIP_REGISTRATION_REJECTED_TEMPORARY] = "Rejected",
179 [SIP_REGISTRATION_REJECTED_PERMANENT] = "Rejected",
180 [SIP_REGISTRATION_STOPPED] = "Stopped",
183 /*! \brief Outbound registration client state information (persists for lifetime of regc) */
184 struct sip_outbound_registration_client_state {
185 /*! \brief Current status of this registration */
186 enum sip_outbound_registration_status status;
187 /*! \brief Outbound registration client */
189 /*! \brief Timer entry for retrying on temporal responses */
190 pj_timer_entry timer;
191 /*! \brief Current number of retries */
192 unsigned int retries;
193 /*! \brief Maximum number of retries permitted */
194 unsigned int max_retries;
195 /*! \brief Interval at which retries should occur for temporal responses */
196 unsigned int retry_interval;
197 /*! \brief Interval at which retries should occur for permanent responses */
198 unsigned int forbidden_retry_interval;
199 /*! \brief Treat authentication challenges that we cannot handle as permanent failures */
200 unsigned int auth_rejection_permanent;
201 /*! \brief Determines whether SIP Path support should be advertised */
202 unsigned int support_path;
203 /*! \brief Serializer for stuff and things */
204 struct ast_taskprocessor *serializer;
205 /*! \brief Configured authentication credentials */
206 struct ast_sip_auth_vector outbound_auths;
207 /*! \brief Registration should be destroyed after completion of transaction */
208 unsigned int destroy:1;
211 /*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
212 struct sip_outbound_registration_state {
213 /*! \brief Client state information */
214 struct sip_outbound_registration_client_state *client_state;
217 /*! \brief Outbound registration information */
218 struct sip_outbound_registration {
219 /*! \brief Sorcery object details */
220 SORCERY_OBJECT(details);
221 /*! \brief Stringfields */
222 AST_DECLARE_STRING_FIELDS(
223 /*! \brief URI for the registrar */
224 AST_STRING_FIELD(server_uri);
225 /*! \brief URI for the AOR */
226 AST_STRING_FIELD(client_uri);
227 /*! \brief Optional user for contact header */
228 AST_STRING_FIELD(contact_user);
229 /*! \brief Explicit transport to use for registration */
230 AST_STRING_FIELD(transport);
231 /*! \brief Outbound proxy to use */
232 AST_STRING_FIELD(outbound_proxy);
234 /*! \brief Requested expiration time */
235 unsigned int expiration;
236 /*! \brief Interval at which retries should occur for temporal responses */
237 unsigned int retry_interval;
238 /*! \brief Interval at which retries should occur for permanent responses */
239 unsigned int forbidden_retry_interval;
240 /*! \brief Treat authentication challenges that we cannot handle as permanent failures */
241 unsigned int auth_rejection_permanent;
242 /*! \brief Maximum number of retries permitted */
243 unsigned int max_retries;
244 /*! \brief Outbound registration state */
245 struct sip_outbound_registration_state *state;
246 /*! \brief Configured authentication credentials */
247 struct ast_sip_auth_vector outbound_auths;
248 /*! \brief Whether Path support is enabled */
249 unsigned int support_path;
252 /*! \brief Helper function which cancels the timer on a client */
253 static void cancel_registration(struct sip_outbound_registration_client_state *client_state)
255 if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &client_state->timer)) {
256 /* The timer was successfully cancelled, drop the refcount of client_state */
257 ao2_ref(client_state, -1);
261 static pj_str_t PATH_NAME = { "path", 4 };
263 /*! \brief Callback function for registering */
264 static int handle_client_registration(void *data)
266 RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
267 pjsip_tx_data *tdata;
268 pjsip_regc_info info;
269 char server_uri[PJSIP_MAX_URL_SIZE], client_uri[PJSIP_MAX_URL_SIZE];
271 cancel_registration(client_state);
273 if ((client_state->status == SIP_REGISTRATION_STOPPED) ||
274 (pjsip_regc_register(client_state->client, PJ_FALSE, &tdata) != PJ_SUCCESS)) {
278 pjsip_regc_get_info(client_state->client, &info);
279 ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
280 ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
281 ast_debug(3, "REGISTER attempt %d to '%s' with client '%s'\n",
282 client_state->retries + 1, server_uri, client_uri);
284 if (client_state->support_path) {
285 pjsip_supported_hdr *hdr;
287 hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL);
289 /* insert a new Supported header */
290 hdr = pjsip_supported_hdr_create(tdata->pool);
295 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
298 /* add on to the existing Supported header */
299 pj_strassign(&hdr->values[hdr->count++], &PATH_NAME);
302 /* Due to the registration the callback may now get called, so bump the ref count */
303 ao2_ref(client_state, +1);
304 if (pjsip_regc_send(client_state->client, tdata) != PJ_SUCCESS) {
305 ao2_ref(client_state, -1);
311 /*! \brief Timer callback function, used just for registrations */
312 static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
314 struct sip_outbound_registration_client_state *client_state = entry->user_data;
316 ao2_ref(client_state, +1);
317 if (ast_sip_push_task(client_state->serializer, handle_client_registration, client_state)) {
318 ast_log(LOG_WARNING, "Failed to pass outbound registration to threadpool\n");
319 ao2_ref(client_state, -1);
325 /*! \brief Helper function which sets up the timer to re-register in a specific amount of time */
326 static void schedule_registration(struct sip_outbound_registration_client_state *client_state, unsigned int seconds)
328 pj_time_val delay = { .sec = seconds, };
330 cancel_registration(client_state);
332 ao2_ref(client_state, +1);
333 if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &client_state->timer, &delay) != PJ_SUCCESS) {
334 ast_log(LOG_WARNING, "Failed to pass timed registration to scheduler\n");
335 ao2_ref(client_state, -1);
339 /*! \brief Callback function for unregistering (potentially) and destroying state */
340 static int handle_client_state_destruction(void *data)
342 RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
344 cancel_registration(client_state);
346 if (client_state->client) {
347 pjsip_regc_info info;
349 pjsip_regc_get_info(client_state->client, &info);
351 if (info.is_busy == PJ_TRUE) {
352 /* If a client transaction is in progress we defer until it is complete */
353 client_state->destroy = 1;
357 if (client_state->status != SIP_REGISTRATION_UNREGISTERED && client_state->status != SIP_REGISTRATION_REJECTED_PERMANENT) {
358 pjsip_tx_data *tdata;
360 if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS) {
361 pjsip_regc_send(client_state->client, tdata);
365 pjsip_regc_destroy(client_state->client);
368 client_state->status = SIP_REGISTRATION_STOPPED;
369 ast_sip_auth_vector_destroy(&client_state->outbound_auths);
374 /*! \brief Structure for registration response */
375 struct registration_response {
376 /*! \brief Response code for the registration attempt */
378 /*! \brief Expiration time for registration */
380 /*! \brief Retry-After value */
382 /*! \brief Outbound registration client state */
383 struct sip_outbound_registration_client_state *client_state;
384 /*! \brief The response message */
385 pjsip_rx_data *rdata;
386 /*! \brief The response transaction */
387 pjsip_transaction *tsx;
390 /*! \brief Registration response structure destructor */
391 static void registration_response_destroy(void *obj)
393 struct registration_response *response = obj;
395 if (response->rdata) {
396 pjsip_rx_data_free_cloned(response->rdata);
399 ao2_cleanup(response->client_state);
402 /* \brief Helper funtion which determines if a response code is temporal or not */
403 static int sip_outbound_registration_is_temporal(unsigned int code,
404 struct sip_outbound_registration_client_state *client_state)
406 /* Shamelessly taken from pjsua */
407 if (code == PJSIP_SC_REQUEST_TIMEOUT ||
408 code == PJSIP_SC_INTERNAL_SERVER_ERROR ||
409 code == PJSIP_SC_BAD_GATEWAY ||
410 code == PJSIP_SC_SERVICE_UNAVAILABLE ||
411 code == PJSIP_SC_SERVER_TIMEOUT ||
412 ((code == PJSIP_SC_UNAUTHORIZED ||
413 code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED) &&
414 !client_state->auth_rejection_permanent) ||
415 PJSIP_IS_STATUS_IN_CLASS(code, 600)) {
422 static void schedule_retry(struct registration_response *response, unsigned int interval,
423 const char *server_uri, const char *client_uri)
425 response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
426 schedule_registration(response->client_state, interval);
428 if (response->rdata) {
429 ast_log(LOG_WARNING, "Temporal response '%d' received from '%s' on "
430 "registration attempt to '%s', retrying in '%d'\n",
431 response->code, server_uri, client_uri, interval);
433 ast_log(LOG_WARNING, "No response received from '%s' on "
434 "registration attempt to '%s', retrying in '%d'\n",
435 server_uri, client_uri, interval);
439 /*! \brief Callback function for handling a response to a registration attempt */
440 static int handle_registration_response(void *data)
442 RAII_VAR(struct registration_response *, response, data, ao2_cleanup);
443 pjsip_regc_info info;
444 char server_uri[PJSIP_MAX_URL_SIZE], client_uri[PJSIP_MAX_URL_SIZE];
446 if (response->client_state->status == SIP_REGISTRATION_STOPPED) {
450 pjsip_regc_get_info(response->client_state->client, &info);
451 ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
452 ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
454 if (response->code == 401 || response->code == 407) {
455 pjsip_tx_data *tdata;
456 if (!ast_sip_create_request_with_auth(&response->client_state->outbound_auths,
457 response->rdata, response->tsx, &tdata)) {
458 ao2_ref(response->client_state, +1);
459 if (pjsip_regc_send(response->client_state->client, tdata) != PJ_SUCCESS) {
460 ao2_cleanup(response->client_state);
464 /* Otherwise, fall through so the failure is processed appropriately */
467 if (PJSIP_IS_STATUS_IN_CLASS(response->code, 200)) {
468 /* If the registration went fine simply reschedule registration for the future */
469 ast_debug(1, "Outbound registration to '%s' with client '%s' successful\n", server_uri, client_uri);
470 response->client_state->status = SIP_REGISTRATION_REGISTERED;
471 response->client_state->retries = 0;
472 schedule_registration(response->client_state, response->expiration - REREGISTER_BUFFER_TIME);
473 } else if (response->retry_after) {
474 /* If we have been instructed to retry after a period of time, schedule it as such */
475 schedule_retry(response, response->retry_after, server_uri, client_uri);
476 } else if (response->client_state->retry_interval && sip_outbound_registration_is_temporal(response->code, response->client_state)) {
477 if (response->client_state->retries == response->client_state->max_retries) {
478 /* If we received enough temporal responses to exceed our maximum give up permanently */
479 response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
480 ast_log(LOG_WARNING, "Maximum retries reached when attempting outbound registration to '%s' with client '%s', stopping registration attempt\n",
481 server_uri, client_uri);
483 /* On the other hand if we can still try some more do so */
484 response->client_state->retries++;
485 schedule_retry(response, response->client_state->retry_interval, server_uri, client_uri);
488 if (response->code == 403
489 && response->client_state->forbidden_retry_interval
490 && response->client_state->retries < response->client_state->max_retries) {
491 /* A forbidden response retry interval is configured and there are retries remaining */
492 response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
493 response->client_state->retries++;
494 schedule_registration(response->client_state, response->client_state->forbidden_retry_interval);
495 ast_log(LOG_WARNING, "403 Forbidden fatal response received from '%s' on registration attempt to '%s', retrying in '%d' seconds\n",
496 server_uri, client_uri, response->client_state->forbidden_retry_interval);
498 /* Finally if there's no hope of registering give up */
499 response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
500 if (response->rdata) {
501 ast_log(LOG_WARNING, "Fatal response '%d' received from '%s' on registration attempt to '%s', stopping outbound registration\n",
502 response->code, server_uri, client_uri);
504 ast_log(LOG_WARNING, "Fatal registration attempt to '%s', stopping outbound registration\n", client_uri);
509 ast_system_publish_registry("PJSIP", client_uri, server_uri, sip_outbound_registration_status_str[response->client_state->status], NULL);
511 /* If deferred destruction is in use see if we need to destroy now */
512 if (response->client_state->destroy) {
513 handle_client_state_destruction(response->client_state);
519 /*! \brief Callback function for outbound registration client */
520 static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *param)
522 RAII_VAR(struct sip_outbound_registration_client_state *, client_state, param->token, ao2_cleanup);
523 struct registration_response *response = ao2_alloc(sizeof(*response), registration_response_destroy);
525 response->code = param->code;
526 response->expiration = param->expiration;
527 response->client_state = client_state;
528 ao2_ref(response->client_state, +1);
531 struct pjsip_retry_after_hdr *retry_after = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_RETRY_AFTER, NULL);
533 response->retry_after = retry_after ? retry_after->ivalue : 0;
534 response->tsx = pjsip_rdata_get_tsx(param->rdata);
535 pjsip_rx_data_clone(param->rdata, 0, &response->rdata);
538 if (ast_sip_push_task(client_state->serializer, handle_registration_response, response)) {
539 ast_log(LOG_WARNING, "Failed to pass incoming registration response to threadpool\n");
540 ao2_cleanup(response);
544 /*! \brief Destructor function for registration state */
545 static void sip_outbound_registration_state_destroy(void *obj)
547 struct sip_outbound_registration_state *state = obj;
549 if (!state->client_state) {
553 if (state->client_state->serializer && ast_sip_push_task(state->client_state->serializer, handle_client_state_destruction, state->client_state)) {
554 ast_log(LOG_WARNING, "Failed to pass outbound registration client destruction to threadpool\n");
555 ao2_ref(state->client_state, -1);
559 /*! \brief Destructor function for client registration state */
560 static void sip_outbound_registration_client_state_destroy(void *obj)
562 struct sip_outbound_registration_client_state *client_state = obj;
564 ast_taskprocessor_unreference(client_state->serializer);
567 /*! \brief Allocator function for registration state */
568 static struct sip_outbound_registration_state *sip_outbound_registration_state_alloc(void)
570 struct sip_outbound_registration_state *state = ao2_alloc(sizeof(*state), sip_outbound_registration_state_destroy);
572 if (!state || !(state->client_state = ao2_alloc(sizeof(*state->client_state), sip_outbound_registration_client_state_destroy))) {
577 if (!(state->client_state->serializer = ast_sip_create_serializer())) {
578 ao2_cleanup(state->client_state);
583 state->client_state->status = SIP_REGISTRATION_UNREGISTERED;
584 state->client_state->timer.user_data = state->client_state;
585 state->client_state->timer.cb = sip_outbound_registration_timer_cb;
590 /*! \brief Destructor function for registration information */
591 static void sip_outbound_registration_destroy(void *obj)
593 struct sip_outbound_registration *registration = obj;
595 ao2_cleanup(registration->state);
596 ast_sip_auth_vector_destroy(®istration->outbound_auths);
598 ast_string_field_free_memory(registration);
601 /*! \brief Allocator function for registration information */
602 static void *sip_outbound_registration_alloc(const char *name)
604 struct sip_outbound_registration *registration = ast_sorcery_generic_alloc(sizeof(*registration), sip_outbound_registration_destroy);
606 if (!registration || ast_string_field_init(registration, 256)) {
607 ao2_cleanup(registration);
614 /*! \brief Helper function which populates a pj_str_t with a contact header */
615 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)
617 pj_str_t tmp, local_addr;
619 pjsip_sip_uri *sip_uri;
620 pjsip_transport_type_e type = PJSIP_TRANSPORT_UNSPECIFIED;
623 pj_strdup_with_null(pool, &tmp, target);
625 if (!(uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0)) ||
626 (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))) {
630 sip_uri = pjsip_uri_get_uri(uri);
632 if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri)) {
633 type = PJSIP_TRANSPORT_TLS;
634 } else if (!sip_uri->transport_param.slen) {
635 type = PJSIP_TRANSPORT_UDP;
637 type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
640 if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
644 if (pj_strchr(&sip_uri->host, ':')) {
645 type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
648 if (pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), pool, type, selector,
649 &local_addr, &local_port) != PJ_SUCCESS) {
653 if (!pj_strchr(&sip_uri->host, ':') && pj_strchr(&local_addr, ':')) {
654 type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
657 contact->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
658 contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
659 "<%s:%s@%s%.*s%s:%d%s%s>",
660 (pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE) ? "sips" : "sip",
662 (type & PJSIP_TRANSPORT_IPV6) ? "[" : "",
663 (int)local_addr.slen,
665 (type & PJSIP_TRANSPORT_IPV6) ? "]" : "",
667 (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? ";transport=" : "",
668 (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? pjsip_transport_get_type_name(type) : "");
675 * \brief Check if a registration can be reused
677 * This checks if the existing outbound registration's configuration differs from a newly-applied
678 * outbound registration to see if the applied one.
680 * \param existing The pre-existing outbound registration
681 * \param applied The newly-created registration
683 static int can_reuse_registration(struct sip_outbound_registration *existing, struct sip_outbound_registration *applied)
687 if (strcmp(existing->server_uri, applied->server_uri) || strcmp(existing->client_uri, applied->client_uri) ||
688 strcmp(existing->transport, applied->transport) || strcmp(existing->contact_user, applied->contact_user) ||
689 strcmp(existing->outbound_proxy, applied->outbound_proxy) ||
690 AST_VECTOR_SIZE(&existing->outbound_auths) != AST_VECTOR_SIZE(&applied->outbound_auths) ||
691 existing->auth_rejection_permanent != applied->auth_rejection_permanent) {
695 for (i = 0; i < AST_VECTOR_SIZE(&existing->outbound_auths); ++i) {
696 if (strcmp(AST_VECTOR_GET(&existing->outbound_auths, i), AST_VECTOR_GET(&applied->outbound_auths, i))) {
704 /*! \brief Helper function that allocates a pjsip registration client and configures it */
705 static int sip_outbound_registration_regc_alloc(void *data)
707 struct sip_outbound_registration *registration = data;
711 pj_str_t server_uri, client_uri, contact_uri;
712 pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
714 pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "URI Validation", 256, 256);
716 ast_log(LOG_ERROR, "Could not create pool for URI validation on outbound registration '%s'\n",
717 ast_sorcery_object_get_id(registration));
721 pj_strdup2_with_null(pool, &tmp, registration->server_uri);
722 uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
724 ast_log(LOG_ERROR, "Invalid server URI '%s' specified on outbound registration '%s'\n",
725 registration->server_uri, ast_sorcery_object_get_id(registration));
726 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
730 pj_strdup2_with_null(pool, &tmp, registration->client_uri);
731 uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
733 ast_log(LOG_ERROR, "Invalid client URI '%s' specified on outbound registration '%s'\n",
734 registration->client_uri, ast_sorcery_object_get_id(registration));
735 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
739 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
741 if (!ast_strlen_zero(registration->transport)) {
742 RAII_VAR(struct ast_sip_transport *, transport, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", registration->transport), ao2_cleanup);
744 if (!transport || !transport->state) {
745 ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport '%s' "
746 " for outbound registration", registration->transport);
750 if (transport->state->transport) {
751 selector.type = PJSIP_TPSELECTOR_TRANSPORT;
752 selector.u.transport = transport->state->transport;
753 } else if (transport->state->factory) {
754 selector.type = PJSIP_TPSELECTOR_LISTENER;
755 selector.u.listener = transport->state->factory;
761 if (!registration->state->client_state->client &&
762 pjsip_regc_create(ast_sip_get_pjsip_endpoint(), registration->state->client_state, sip_outbound_registration_response_cb,
763 ®istration->state->client_state->client) != PJ_SUCCESS) {
767 pjsip_regc_set_transport(registration->state->client_state->client, &selector);
769 if (!ast_strlen_zero(registration->outbound_proxy)) {
770 pjsip_route_hdr route_set, *route;
771 static const pj_str_t ROUTE_HNAME = { "Route", 5 };
774 pj_list_init(&route_set);
776 pj_strdup2_with_null(pjsip_regc_get_pool(registration->state->client_state->client), &tmp, registration->outbound_proxy);
777 if (!(route = pjsip_parse_hdr(pjsip_regc_get_pool(registration->state->client_state->client), &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL))) {
780 pj_list_insert_nodes_before(&route_set, route);
782 pjsip_regc_set_route_set(registration->state->client_state->client, &route_set);
785 pj_cstr(&server_uri, registration->server_uri);
787 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)) {
791 pj_cstr(&client_uri, registration->client_uri);
793 if (pjsip_regc_init(registration->state->client_state->client, &server_uri, &client_uri, &client_uri, 1, &contact_uri, registration->expiration) != PJ_SUCCESS) {
800 /*! \brief Apply function which finds or allocates a state structure */
801 static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, void *obj)
803 RAII_VAR(struct sip_outbound_registration *, existing, ast_sorcery_retrieve_by_id(sorcery, "registration", ast_sorcery_object_get_id(obj)), ao2_cleanup);
804 struct sip_outbound_registration *applied = obj;
806 if (ast_strlen_zero(applied->server_uri)) {
807 ast_log(LOG_ERROR, "No server URI specified on outbound registration '%s'",
808 ast_sorcery_object_get_id(applied));
810 } else if (ast_strlen_zero(applied->client_uri)) {
811 ast_log(LOG_ERROR, "No client URI specified on outbound registration '%s'\n",
812 ast_sorcery_object_get_id(applied));
817 /* If no existing registration exists we can just start fresh easily */
818 applied->state = sip_outbound_registration_state_alloc();
820 /* If there is an existing registration things are more complicated, we can immediately reuse this state if most stuff remains unchanged */
821 if (can_reuse_registration(existing, applied)) {
822 applied->state = existing->state;
823 ao2_ref(applied->state, +1);
826 applied->state = sip_outbound_registration_state_alloc();
829 if (!applied->state) {
833 return ast_sip_push_task_synchronous(NULL, sip_outbound_registration_regc_alloc, applied);
836 /*! \brief Helper function which performs a single registration */
837 static int sip_outbound_registration_perform(void *data)
839 RAII_VAR(struct sip_outbound_registration *, registration, data, ao2_cleanup);
842 /* Just in case the client state is being reused for this registration, free the auth information */
843 ast_sip_auth_vector_destroy(®istration->state->client_state->outbound_auths);
845 AST_VECTOR_INIT(®istration->state->client_state->outbound_auths, AST_VECTOR_SIZE(®istration->outbound_auths));
846 for (i = 0; i < AST_VECTOR_SIZE(®istration->outbound_auths); ++i) {
847 const char *name = ast_strdup(AST_VECTOR_GET(®istration->outbound_auths, i));
848 AST_VECTOR_APPEND(®istration->state->client_state->outbound_auths, name);
850 registration->state->client_state->retry_interval = registration->retry_interval;
851 registration->state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval;
852 registration->state->client_state->max_retries = registration->max_retries;
853 registration->state->client_state->retries = 0;
854 registration->state->client_state->support_path = registration->support_path;
856 pjsip_regc_update_expires(registration->state->client_state->client, registration->expiration);
858 schedule_registration(registration->state->client_state, (ast_random() % 10) + 1);
863 /*! \brief Helper function which performs all registrations */
864 static void sip_outbound_registration_perform_all(void)
866 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);
867 struct ao2_iterator i;
868 struct sip_outbound_registration *registration;
870 if (!registrations) {
874 i = ao2_iterator_init(registrations, 0);
875 while ((registration = ao2_iterator_next(&i))) {
876 if (ast_sip_push_task(registration->state->client_state->serializer, sip_outbound_registration_perform, registration)) {
877 ast_log(LOG_ERROR, "Failed to perform outbound registration on '%s'\n", ast_sorcery_object_get_id(registration));
878 ao2_ref(registration, -1);
881 ao2_iterator_destroy(&i);
884 static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
886 struct sip_outbound_registration *registration = obj;
888 return ast_sip_auth_vector_init(®istration->outbound_auths, var->value);
891 static int outbound_auths_to_str(const void *obj, const intptr_t *args, char **buf)
893 const struct sip_outbound_registration *registration = obj;
895 return ast_sip_auths_to_str(®istration->outbound_auths, buf);
898 static int outbound_auths_to_var_list(const void *obj, struct ast_variable **fields)
900 const struct sip_outbound_registration *registration = obj;
902 struct ast_variable *head = NULL;
904 for (i = 0; i < AST_VECTOR_SIZE(®istration->outbound_auths) ; i++) {
905 ast_variable_list_append(&head, ast_variable_new("outbound_auth",
906 AST_VECTOR_GET(®istration->outbound_auths, i), ""));
916 static struct sip_outbound_registration *retrieve_registration(const char *registration_name)
918 return ast_sorcery_retrieve_by_id(
919 ast_sip_get_sorcery(),
924 static int unregister_task(void *obj)
926 RAII_VAR(struct sip_outbound_registration*, registration, obj, ao2_cleanup);
927 struct pjsip_regc *client = registration->state->client_state->client;
928 pjsip_tx_data *tdata;
930 if (pjsip_regc_unregister(client, &tdata) != PJ_SUCCESS) {
934 ao2_ref(registration->state->client_state, +1);
935 if (pjsip_regc_send(client, tdata) != PJ_SUCCESS) {
936 ao2_cleanup(registration->state->client_state);
942 static int queue_unregister(struct sip_outbound_registration *registration)
944 ao2_ref(registration, +1);
945 if (ast_sip_push_task(registration->state->client_state->serializer, unregister_task, registration)) {
946 ao2_cleanup(registration);
952 static char *cli_complete_registration(const char *line, const char *word,
958 struct sip_outbound_registration *registration;
959 RAII_VAR(struct ao2_container *, registrations, NULL, ao2_cleanup);
960 struct ao2_iterator i;
966 wordlen = strlen(word);
967 registrations = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration",
968 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
969 if (!registrations) {
973 i = ao2_iterator_init(registrations, 0);
974 while ((registration = ao2_iterator_next(&i))) {
975 const char *name = ast_sorcery_object_get_id(registration);
976 if (!strncasecmp(word, name, wordlen) && ++which > state) {
977 result = ast_strdup(name);
980 ao2_cleanup(registration);
985 ao2_iterator_destroy(&i);
989 static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
991 RAII_VAR(struct sip_outbound_registration *, registration, NULL, ao2_cleanup);
992 const char *registration_name;
996 e->command = "pjsip send unregister";
998 "Usage: pjsip send unregister <registration>\n"
999 " Send a SIP REGISTER request to the specified outbound "
1000 "registration with an expiration of 0. This will cause the contact "
1001 "added by this registration to be removed on the remote system.\n";
1004 return cli_complete_registration(a->line, a->word, a->pos, a->n);
1008 return CLI_SHOWUSAGE;
1011 registration_name = a->argv[3];
1013 registration = retrieve_registration(registration_name);
1014 if (!registration) {
1015 ast_cli(a->fd, "Unable to retrieve registration %s\n", registration_name);
1019 if (queue_unregister(registration)) {
1020 ast_cli(a->fd, "Failed to queue unregistration");
1027 static int ami_unregister(struct mansession *s, const struct message *m)
1029 const char *registration_name = astman_get_header(m, "Registration");
1030 RAII_VAR(struct sip_outbound_registration *, registration, NULL, ao2_cleanup);
1032 if (ast_strlen_zero(registration_name)) {
1033 astman_send_error(s, m, "Registration parameter missing.");
1037 registration = retrieve_registration(registration_name);
1038 if (!registration) {
1039 astman_send_error(s, m, "Unable to retrieve registration entry\n");
1044 if (queue_unregister(registration)) {
1045 astman_send_ack(s, m, "Failed to queue unregistration");
1049 astman_send_ack(s, m, "Unregistration sent");
1053 struct sip_ami_outbound {
1054 struct ast_sip_ami *ami;
1057 struct sip_outbound_registration *registration;
1060 static int ami_outbound_registration_task(void *obj)
1062 struct sip_ami_outbound *ami = obj;
1063 RAII_VAR(struct ast_str *, buf,
1064 ast_sip_create_ami_event("OutboundRegistrationDetail", ami->ami), ast_free);
1070 ast_sip_sorcery_object_to_ami(ami->registration, &buf);
1072 if (ami->registration->state) {
1073 pjsip_regc_info info;
1074 if (ami->registration->state->client_state->status ==
1075 SIP_REGISTRATION_REGISTERED) {
1078 ++ami->not_registered;
1081 ast_str_append(&buf, 0, "Status: %s%s",
1082 sip_outbound_registration_status_str[
1083 ami->registration->state->client_state->status], "\r\n");
1085 pjsip_regc_get_info(ami->registration->state->client_state->client, &info);
1086 ast_str_append(&buf, 0, "NextReg: %d%s", info.next_reg, "\r\n");
1089 astman_append(ami->ami->s, "%s\r\n", ast_str_buffer(buf));
1090 return ast_sip_format_auths_ami(&ami->registration->outbound_auths, ami->ami);
1093 static int ami_outbound_registration_detail(void *obj, void *arg, int flags)
1095 struct sip_ami_outbound *ami = arg;
1097 ami->registration = obj;
1098 return ast_sip_push_task_synchronous(
1099 NULL, ami_outbound_registration_task, ami);
1102 static int ami_show_outbound_registrations(struct mansession *s,
1103 const struct message *m)
1105 struct ast_sip_ami ami = { .s = s, .m = m };
1106 struct sip_ami_outbound ami_outbound = { .ami = &ami };
1107 RAII_VAR(struct ao2_container *, regs, ast_sorcery_retrieve_by_fields(
1108 ast_sip_get_sorcery(), "registration", AST_RETRIEVE_FLAG_MULTIPLE |
1109 AST_RETRIEVE_FLAG_ALL, NULL), ao2_cleanup);
1112 astman_send_error(s, m, "Unable to retreive "
1113 "outbound registrations\n");
1117 astman_send_listack(s, m, "Following are Events for each Outbound "
1118 "registration", "start");
1120 ao2_callback(regs, OBJ_NODATA, ami_outbound_registration_detail, &ami_outbound);
1123 "Event: OutboundRegistrationDetailComplete\r\n"
1124 "EventList: Complete\r\n"
1125 "Registered: %d\r\n"
1126 "NotRegistered: %d\r\n\r\n",
1127 ami_outbound.registered,
1128 ami_outbound.not_registered);
1132 static struct ao2_container *cli_get_container(void)
1134 RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
1135 RAII_VAR(struct ao2_container *, s_container, NULL, ao2_cleanup);
1137 container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration",
1138 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
1143 s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
1144 ast_sorcery_object_id_compare, NULL);
1149 if (ao2_container_dup(s_container, container, 0)) {
1152 ao2_ref(s_container, +1);
1156 static int cli_iterator(const void *container, ao2_callback_fn callback, void *args)
1158 struct ao2_container *ao2container = (struct ao2_container *) container;
1160 ao2_callback(ao2container, OBJ_NODATA, callback, args);
1164 static int cli_print_header(void *obj, void *arg, int flags)
1166 struct ast_sip_cli_context *context = arg;
1168 if (!context->output_buffer) {
1172 ast_str_append(&context->output_buffer, 0,
1173 " <Registration/ServerURI..............................> <Auth..........> <Status.......>\n");
1178 static int cli_print_body(void *obj, void *arg, int flags)
1180 struct sip_outbound_registration *registration = obj;
1181 struct ast_sip_cli_context *context = arg;
1182 const char *id = ast_sorcery_object_get_id(registration);
1183 #define REGISTRATION_URI_FIELD_LEN 53
1185 if (!context->output_buffer) {
1189 ast_str_append(&context->output_buffer, 0, " %-s/%-*.*s %-16s %-16s\n",
1191 (int) (REGISTRATION_URI_FIELD_LEN - strlen(id)),
1192 (int) (REGISTRATION_URI_FIELD_LEN - strlen(id)),
1193 registration->server_uri,
1194 AST_VECTOR_SIZE(®istration->outbound_auths)
1195 ? AST_VECTOR_GET(®istration->outbound_auths, 0)
1197 sip_outbound_registration_status_str[registration->state->client_state->status]);
1199 if (context->show_details
1200 || (context->show_details_only_level_0 && context->indent_level == 0)) {
1201 ast_str_append(&context->output_buffer, 0, "\n");
1202 ast_sip_cli_print_sorcery_objectset(registration, context, 0);
1208 static struct ast_sip_cli_formatter_entry cli_formatter = {
1209 .name = "registration",
1210 .print_header = cli_print_header,
1211 .print_body = cli_print_body,
1212 .get_container = cli_get_container,
1213 .iterator = cli_iterator,
1214 .comparator = ast_sorcery_object_id_compare,
1218 * A function pointer to callback needs to be within the
1219 * module in order to avoid problems with an undefined
1220 * symbol when the module is loaded.
1222 static char *my_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1224 return ast_sip_cli_traverse_objects(e, cmd, a);
1227 static struct ast_cli_entry cli_outbound_registration[] = {
1228 AST_CLI_DEFINE(cli_unregister, "Send a REGISTER request to an outbound registration target with a expiration of 0"),
1229 AST_CLI_DEFINE(my_cli_traverse_objects, "List PJSIP Registrations",
1230 .command = "pjsip list registrations",
1231 .usage = "Usage: pjsip list registrations\n"
1232 " List the configured PJSIP Registrations\n"),
1233 AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Registrations",
1234 .command = "pjsip show registrations",
1235 .usage = "Usage: pjsip show registrations\n"
1236 " Show the configured PJSIP Registrations\n"),
1237 AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Registration",
1238 .command = "pjsip show registration",
1239 .usage = "Usage: pjsip show registration <id>\n"
1240 " Show the configured PJSIP Registration\n"),
1243 static int load_module(void)
1245 ast_sorcery_apply_default(ast_sip_get_sorcery(), "registration", "config", "pjsip.conf,criteria=type=registration");
1247 if (ast_sorcery_object_register(ast_sip_get_sorcery(), "registration", sip_outbound_registration_alloc, NULL, sip_outbound_registration_apply)) {
1248 return AST_MODULE_LOAD_DECLINE;
1251 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "type", "", OPT_NOOP_T, 0, 0);
1252 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "server_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, server_uri));
1253 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "client_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, client_uri));
1254 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_user));
1255 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, transport));
1256 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, outbound_proxy));
1257 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, expiration));
1258 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_interval", "60", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_interval));
1259 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));
1260 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "max_retries", "10", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, max_retries));
1261 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));
1262 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);
1263 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, support_path));
1264 ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration");
1265 sip_outbound_registration_perform_all();
1267 ast_cli_register_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
1268 ast_manager_register_xml("PJSIPUnregister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_unregister);
1269 ast_manager_register_xml("PJSIPShowRegistrationsOutbound", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING,ami_show_outbound_registrations);
1270 ast_sip_register_cli_formatter(&cli_formatter);
1272 return AST_MODULE_LOAD_SUCCESS;
1275 static int reload_module(void)
1277 ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration");
1278 sip_outbound_registration_perform_all();
1282 static int unload_module(void)
1284 ast_sip_unregister_cli_formatter(&cli_formatter);
1285 ast_cli_unregister_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
1286 ast_manager_unregister("PJSIPShowRegistrationsOutbound");
1287 ast_manager_unregister("PJSIPUnregister");
1291 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Outbound Registration Support",
1292 .load = load_module,
1293 .reload = reload_module,
1294 .unload = unload_module,
1295 .load_pri = AST_MODPRI_APP_DEPEND,