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/module.h"
32 #include "asterisk/taskprocessor.h"
33 #include "asterisk/cli.h"
34 #include "asterisk/stasis_system.h"
37 <configInfo name="res_pjsip_outbound_registration" language="en_US">
38 <synopsis>SIP resource for outbound registrations</synopsis>
40 <emphasis>Outbound Registration</emphasis>
42 <para>This module allows <literal>res_pjsip</literal> to register to other SIP servers.</para>
44 <configFile name="pjsip.conf">
45 <configObject name="registration">
46 <synopsis>The configuration for outbound registration</synopsis>
48 Registration is <emphasis>COMPLETELY</emphasis> separate from the rest of
49 <literal>pjsip.conf</literal>. A minimal configuration consists of
50 setting a <literal>server_uri</literal> and a <literal>client_uri</literal>.
52 <configOption name="auth_rejection_permanent" default="yes">
53 <synopsis>Determines whether failed authentication challenges are treated
54 as permanent failures.</synopsis>
55 <description><para>If this option is enabled and an authentication challenge fails,
56 registration will not be attempted again until the configuration is reloaded.</para></description>
58 <configOption name="client_uri">
59 <synopsis>Client SIP URI used when attemping outbound registration</synopsis>
61 This is the address-of-record for the outbound registration (i.e. the URI in
62 the To header of the REGISTER).</para>
63 <para>For registration with an ITSP, the client SIP URI may need to consist of
64 an account name or number and the provider's hostname for their registrar, e.g.
65 client_uri=1234567890@example.com. This may differ between providers.</para>
66 <para>For registration to generic registrars, the client SIP URI will depend
67 on networking specifics and configuration of the registrar.
70 <configOption name="contact_user">
71 <synopsis>Contact User to use in request</synopsis>
73 <configOption name="expiration" default="3600">
74 <synopsis>Expiration time for registrations in seconds</synopsis>
76 <configOption name="max_retries" default="10">
77 <synopsis>Maximum number of registration attempts.</synopsis>
79 <configOption name="outbound_auth" default="">
80 <synopsis>Authentication object to be used for outbound registrations.</synopsis>
82 <configOption name="outbound_proxy" default="">
83 <synopsis>Outbound Proxy used to send registrations</synopsis>
85 <configOption name="retry_interval" default="60">
86 <synopsis>Interval in seconds between retries if outbound registration is unsuccessful</synopsis>
88 <configOption name="forbidden_retry_interval" default="0">
89 <synopsis>Interval used when receiving a 403 Forbidden response.</synopsis>
91 If a 403 Forbidden is received, chan_pjsip will wait
92 <replaceable>forbidden_retry_interval</replaceable> seconds before
93 attempting registration again. If 0 is specified, chan_pjsip will not
94 retry after receiving a 403 Forbidden response. Setting this to a non-zero
95 value goes against a "SHOULD NOT" in RFC3261, but can be used to work around
99 <configOption name="server_uri">
100 <synopsis>SIP URI of the server to register against</synopsis>
102 This is the URI at which to find the registrar to send the outbound REGISTER. This URI
103 is used as the request URI of the outbound REGISTER request from Asterisk.</para>
104 <para>For registration with an ITSP, the setting may often be just the domain of
105 the registrar, e.g. sip:sip.example.com.
106 </para></description>
108 <configOption name="transport">
109 <synopsis>Transport used for outbound authentication</synopsis>
111 <note><para>A <replaceable>transport</replaceable> configured in
112 <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>
115 <configOption name="type">
116 <synopsis>Must be of type 'registration'.</synopsis>
121 <manager name="PJSIPUnregister" language="en_US">
123 Unregister an outbound registration.
126 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
127 <parameter name="Registration" required="true">
128 <para>The outbound registration to unregister.</para>
134 /*! \brief Amount of buffer time (in seconds) before expiration that we re-register at */
135 #define REREGISTER_BUFFER_TIME 10
137 /*! \brief Various states that an outbound registration may be in */
138 enum sip_outbound_registration_status {
139 /*! \brief Currently unregistered */
140 SIP_REGISTRATION_UNREGISTERED = 0,
141 /*! \brief Registered, yay! */
142 SIP_REGISTRATION_REGISTERED,
143 /*! \brief Registration was rejected, but response was temporal */
144 SIP_REGISTRATION_REJECTED_TEMPORARY,
145 /*! \brief Registration was rejected, permanently */
146 SIP_REGISTRATION_REJECTED_PERMANENT,
147 /*! \brief Registration has been stopped */
148 SIP_REGISTRATION_STOPPED,
151 static const char *sip_outbound_registration_status_str[] = {
152 [SIP_REGISTRATION_UNREGISTERED] = "Unregistered",
153 [SIP_REGISTRATION_REGISTERED] = "Registered",
154 [SIP_REGISTRATION_REJECTED_TEMPORARY] = "Rejected",
155 [SIP_REGISTRATION_REJECTED_PERMANENT] = "Rejected",
156 [SIP_REGISTRATION_STOPPED] = "Stopped",
159 /*! \brief Outbound registration client state information (persists for lifetime of regc) */
160 struct sip_outbound_registration_client_state {
161 /*! \brief Current status of this registration */
162 enum sip_outbound_registration_status status;
163 /*! \brief Outbound registration client */
165 /*! \brief Timer entry for retrying on temporal responses */
166 pj_timer_entry timer;
167 /*! \brief Current number of retries */
168 unsigned int retries;
169 /*! \brief Maximum number of retries permitted */
170 unsigned int max_retries;
171 /*! \brief Interval at which retries should occur for temporal responses */
172 unsigned int retry_interval;
173 /*! \brief Interval at which retries should occur for permanent responses */
174 unsigned int forbidden_retry_interval;
175 /*! \brief Treat authentication challenges that we cannot handle as permanent failures */
176 unsigned int auth_rejection_permanent;
177 /*! \brief Serializer for stuff and things */
178 struct ast_taskprocessor *serializer;
179 /*! \brief Configured authentication credentials */
180 struct ast_sip_auth_array outbound_auths;
181 /*! \brief Number of configured auths */
182 size_t num_outbound_auths;
183 /*! \brief Registration should be destroyed after completion of transaction */
184 unsigned int destroy:1;
187 /*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
188 struct sip_outbound_registration_state {
189 /*! \brief Client state information */
190 struct sip_outbound_registration_client_state *client_state;
193 /*! \brief Outbound registration information */
194 struct sip_outbound_registration {
195 /*! \brief Sorcery object details */
196 SORCERY_OBJECT(details);
197 /*! \brief Stringfields */
198 AST_DECLARE_STRING_FIELDS(
199 /*! \brief URI for the registrar */
200 AST_STRING_FIELD(server_uri);
201 /*! \brief URI for the AOR */
202 AST_STRING_FIELD(client_uri);
203 /*! \brief Optional user for contact header */
204 AST_STRING_FIELD(contact_user);
205 /*! \brief Explicit transport to use for registration */
206 AST_STRING_FIELD(transport);
207 /*! \brief Outbound proxy to use */
208 AST_STRING_FIELD(outbound_proxy);
210 /*! \brief Requested expiration time */
211 unsigned int expiration;
212 /*! \brief Interval at which retries should occur for temporal responses */
213 unsigned int retry_interval;
214 /*! \brief Interval at which retries should occur for permanent responses */
215 unsigned int forbidden_retry_interval;
216 /*! \brief Treat authentication challenges that we cannot handle as permanent failures */
217 unsigned int auth_rejection_permanent;
218 /*! \brief Maximum number of retries permitted */
219 unsigned int max_retries;
220 /*! \brief Outbound registration state */
221 struct sip_outbound_registration_state *state;
222 /*! \brief Configured authentication credentials */
223 struct ast_sip_auth_array outbound_auths;
224 /*! \brief Number of configured auths */
225 size_t num_outbound_auths;
228 /*! \brief Helper function which cancels the timer on a client */
229 static void cancel_registration(struct sip_outbound_registration_client_state *client_state)
231 if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &client_state->timer)) {
232 /* The timer was successfully cancelled, drop the refcount of client_state */
233 ao2_ref(client_state, -1);
237 /*! \brief Callback function for registering */
238 static int handle_client_registration(void *data)
240 RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
241 pjsip_tx_data *tdata;
242 pjsip_regc_info info;
243 char server_uri[PJSIP_MAX_URL_SIZE], client_uri[PJSIP_MAX_URL_SIZE];
245 cancel_registration(client_state);
247 if ((client_state->status == SIP_REGISTRATION_STOPPED) ||
248 (pjsip_regc_register(client_state->client, PJ_FALSE, &tdata) != PJ_SUCCESS)) {
252 pjsip_regc_get_info(client_state->client, &info);
253 ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
254 ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
255 ast_debug(3, "REGISTER attempt %d to '%s' with client '%s'\n",
256 client_state->retries + 1, server_uri, client_uri);
258 /* Due to the registration the callback may now get called, so bump the ref count */
259 ao2_ref(client_state, +1);
260 if (pjsip_regc_send(client_state->client, tdata) != PJ_SUCCESS) {
261 ao2_ref(client_state, -1);
267 /*! \brief Timer callback function, used just for registrations */
268 static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
270 struct sip_outbound_registration_client_state *client_state = entry->user_data;
272 ao2_ref(client_state, +1);
273 if (ast_sip_push_task(client_state->serializer, handle_client_registration, client_state)) {
274 ast_log(LOG_WARNING, "Failed to pass outbound registration to threadpool\n");
275 ao2_ref(client_state, -1);
281 /*! \brief Helper function which sets up the timer to re-register in a specific amount of time */
282 static void schedule_registration(struct sip_outbound_registration_client_state *client_state, unsigned int seconds)
284 pj_time_val delay = { .sec = seconds, };
286 cancel_registration(client_state);
288 ao2_ref(client_state, +1);
289 if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &client_state->timer, &delay) != PJ_SUCCESS) {
290 ast_log(LOG_WARNING, "Failed to pass timed registration to scheduler\n");
291 ao2_ref(client_state, -1);
295 /*! \brief Callback function for unregistering (potentially) and destroying state */
296 static int handle_client_state_destruction(void *data)
298 RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
299 pjsip_regc_info info;
301 cancel_registration(client_state);
303 pjsip_regc_get_info(client_state->client, &info);
305 if (info.is_busy == PJ_TRUE) {
306 /* If a client transaction is in progress we defer until it is complete */
307 client_state->destroy = 1;
311 if (client_state->status != SIP_REGISTRATION_UNREGISTERED && client_state->status != SIP_REGISTRATION_REJECTED_PERMANENT) {
312 pjsip_tx_data *tdata;
314 if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS) {
315 pjsip_regc_send(client_state->client, tdata);
319 pjsip_regc_destroy(client_state->client);
321 client_state->status = SIP_REGISTRATION_STOPPED;
322 ast_sip_auth_array_destroy(&client_state->outbound_auths);
327 /*! \brief Structure for registration response */
328 struct registration_response {
329 /*! \brief Response code for the registration attempt */
331 /*! \brief Expiration time for registration */
333 /*! \brief Retry-After value */
335 /*! \brief Outbound registration client state */
336 struct sip_outbound_registration_client_state *client_state;
337 /*! \brief The response message */
338 pjsip_rx_data *rdata;
339 /*! \brief The response transaction */
340 pjsip_transaction *tsx;
343 /*! \brief Registration response structure destructor */
344 static void registration_response_destroy(void *obj)
346 struct registration_response *response = obj;
348 if (response->rdata) {
349 pjsip_rx_data_free_cloned(response->rdata);
352 ao2_cleanup(response->client_state);
355 /* \brief Helper funtion which determines if a response code is temporal or not */
356 static int sip_outbound_registration_is_temporal(unsigned int code,
357 struct sip_outbound_registration_client_state *client_state)
359 /* Shamelessly taken from pjsua */
360 if (code == PJSIP_SC_REQUEST_TIMEOUT ||
361 code == PJSIP_SC_INTERNAL_SERVER_ERROR ||
362 code == PJSIP_SC_BAD_GATEWAY ||
363 code == PJSIP_SC_SERVICE_UNAVAILABLE ||
364 code == PJSIP_SC_SERVER_TIMEOUT ||
365 ((code == PJSIP_SC_UNAUTHORIZED ||
366 code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED) &&
367 !client_state->auth_rejection_permanent) ||
368 PJSIP_IS_STATUS_IN_CLASS(code, 600)) {
375 static void schedule_retry(struct registration_response *response, unsigned int interval,
376 const char *server_uri, const char *client_uri)
378 response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
379 schedule_registration(response->client_state, interval);
381 if (response->rdata) {
382 ast_log(LOG_WARNING, "Temporal response '%d' received from '%s' on "
383 "registration attempt to '%s', retrying in '%d'\n",
384 response->code, server_uri, client_uri, interval);
386 ast_log(LOG_WARNING, "No response received from '%s' on "
387 "registration attempt to '%s', retrying in '%d'\n",
388 server_uri, client_uri, interval);
392 /*! \brief Callback function for handling a response to a registration attempt */
393 static int handle_registration_response(void *data)
395 RAII_VAR(struct registration_response *, response, data, ao2_cleanup);
396 pjsip_regc_info info;
397 char server_uri[PJSIP_MAX_URL_SIZE], client_uri[PJSIP_MAX_URL_SIZE];
399 if (response->client_state->status == SIP_REGISTRATION_STOPPED) {
403 pjsip_regc_get_info(response->client_state->client, &info);
404 ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
405 ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
407 if (response->code == 401 || response->code == 407) {
408 pjsip_tx_data *tdata;
409 if (!ast_sip_create_request_with_auth(&response->client_state->outbound_auths,
410 response->rdata, response->tsx, &tdata)) {
411 ao2_ref(response->client_state, +1);
412 if (pjsip_regc_send(response->client_state->client, tdata) != PJ_SUCCESS) {
413 ao2_cleanup(response->client_state);
417 /* Otherwise, fall through so the failure is processed appropriately */
420 if (PJSIP_IS_STATUS_IN_CLASS(response->code, 200)) {
421 /* If the registration went fine simply reschedule registration for the future */
422 ast_debug(1, "Outbound registration to '%s' with client '%s' successful\n", server_uri, client_uri);
423 response->client_state->status = SIP_REGISTRATION_REGISTERED;
424 response->client_state->retries = 0;
425 schedule_registration(response->client_state, response->expiration - REREGISTER_BUFFER_TIME);
426 } else if (response->retry_after) {
427 /* If we have been instructed to retry after a period of time, schedule it as such */
428 schedule_retry(response, response->retry_after, server_uri, client_uri);
429 } else if (response->client_state->retry_interval && sip_outbound_registration_is_temporal(response->code, response->client_state)) {
430 if (response->client_state->retries == response->client_state->max_retries) {
431 /* If we received enough temporal responses to exceed our maximum give up permanently */
432 response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
433 ast_log(LOG_WARNING, "Maximum retries reached when attempting outbound registration to '%s' with client '%s', stopping registration attempt\n",
434 server_uri, client_uri);
436 /* On the other hand if we can still try some more do so */
437 response->client_state->retries++;
438 schedule_retry(response, response->client_state->retry_interval, server_uri, client_uri);
441 if (response->code == 403
442 && response->client_state->forbidden_retry_interval
443 && response->client_state->retries < response->client_state->max_retries) {
444 /* A forbidden response retry interval is configured and there are retries remaining */
445 response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
446 response->client_state->retries++;
447 schedule_registration(response->client_state, response->client_state->forbidden_retry_interval);
448 ast_log(LOG_WARNING, "403 Forbidden fatal response received from '%s' on registration attempt to '%s', retrying in '%d' seconds\n",
449 server_uri, client_uri, response->client_state->forbidden_retry_interval);
451 /* Finally if there's no hope of registering give up */
452 response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
453 if (response->rdata) {
454 ast_log(LOG_WARNING, "Fatal response '%d' received from '%s' on registration attempt to '%s', stopping outbound registration\n",
455 response->code, server_uri, client_uri);
457 ast_log(LOG_WARNING, "Fatal registration attempt to '%s', stopping outbound registration\n", client_uri);
462 ast_system_publish_registry("PJSIP", client_uri, server_uri, sip_outbound_registration_status_str[response->client_state->status], NULL);
464 /* If deferred destruction is in use see if we need to destroy now */
465 if (response->client_state->destroy) {
466 handle_client_state_destruction(response->client_state);
472 /*! \brief Callback function for outbound registration client */
473 static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *param)
475 RAII_VAR(struct sip_outbound_registration_client_state *, client_state, param->token, ao2_cleanup);
476 struct registration_response *response = ao2_alloc(sizeof(*response), registration_response_destroy);
478 response->code = param->code;
479 response->expiration = param->expiration;
480 response->client_state = client_state;
481 ao2_ref(response->client_state, +1);
484 struct pjsip_retry_after_hdr *retry_after = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_RETRY_AFTER, NULL);
486 response->retry_after = retry_after ? retry_after->ivalue : 0;
487 response->tsx = pjsip_rdata_get_tsx(param->rdata);
488 pjsip_rx_data_clone(param->rdata, 0, &response->rdata);
491 if (ast_sip_push_task(client_state->serializer, handle_registration_response, response)) {
492 ast_log(LOG_WARNING, "Failed to pass incoming registration response to threadpool\n");
493 ao2_cleanup(response);
497 /*! \brief Destructor function for registration state */
498 static void sip_outbound_registration_state_destroy(void *obj)
500 struct sip_outbound_registration_state *state = obj;
502 if (!state->client_state) {
506 if (state->client_state->serializer && ast_sip_push_task(state->client_state->serializer, handle_client_state_destruction, state->client_state)) {
507 ast_log(LOG_WARNING, "Failed to pass outbound registration client destruction to threadpool\n");
508 ao2_ref(state->client_state, -1);
512 /*! \brief Destructor function for client registration state */
513 static void sip_outbound_registration_client_state_destroy(void *obj)
515 struct sip_outbound_registration_client_state *client_state = obj;
517 ast_taskprocessor_unreference(client_state->serializer);
520 /*! \brief Allocator function for registration state */
521 static struct sip_outbound_registration_state *sip_outbound_registration_state_alloc(void)
523 struct sip_outbound_registration_state *state = ao2_alloc(sizeof(*state), sip_outbound_registration_state_destroy);
525 if (!state || !(state->client_state = ao2_alloc(sizeof(*state->client_state), sip_outbound_registration_client_state_destroy))) {
530 if ((pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state->client_state, sip_outbound_registration_response_cb, &state->client_state->client) != PJ_SUCCESS) ||
531 !(state->client_state->serializer = ast_sip_create_serializer())) {
532 /* This is on purpose, normal operation will have it be deallocated within the serializer */
533 pjsip_regc_destroy(state->client_state->client);
534 ao2_cleanup(state->client_state);
539 state->client_state->status = SIP_REGISTRATION_UNREGISTERED;
540 state->client_state->timer.user_data = state->client_state;
541 state->client_state->timer.cb = sip_outbound_registration_timer_cb;
546 /*! \brief Destructor function for registration information */
547 static void sip_outbound_registration_destroy(void *obj)
549 struct sip_outbound_registration *registration = obj;
551 ao2_cleanup(registration->state);
552 ast_sip_auth_array_destroy(®istration->outbound_auths);
554 ast_string_field_free_memory(registration);
557 /*! \brief Allocator function for registration information */
558 static void *sip_outbound_registration_alloc(const char *name)
560 struct sip_outbound_registration *registration = ast_sorcery_generic_alloc(sizeof(*registration), sip_outbound_registration_destroy);
562 if (!registration || ast_string_field_init(registration, 256)) {
563 ao2_cleanup(registration);
570 /*! \brief Helper function which populates a pj_str_t with a contact header */
571 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)
573 pj_str_t tmp, local_addr;
575 pjsip_sip_uri *sip_uri;
576 pjsip_transport_type_e type = PJSIP_TRANSPORT_UNSPECIFIED;
579 pj_strdup_with_null(pool, &tmp, target);
581 if (!(uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0)) ||
582 (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))) {
586 sip_uri = pjsip_uri_get_uri(uri);
588 if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri)) {
589 type = PJSIP_TRANSPORT_TLS;
590 } else if (!sip_uri->transport_param.slen) {
591 type = PJSIP_TRANSPORT_UDP;
593 type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
596 if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
600 if (pj_strchr(&sip_uri->host, ':')) {
601 type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
604 if (pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), pool, type, selector,
605 &local_addr, &local_port) != PJ_SUCCESS) {
609 if (!pj_strchr(&sip_uri->host, ':') && pj_strchr(&local_addr, ':')) {
610 type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
613 contact->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
614 contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
615 "<%s:%s@%s%.*s%s:%d%s%s>",
616 (pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE) ? "sips" : "sip",
618 (type & PJSIP_TRANSPORT_IPV6) ? "[" : "",
619 (int)local_addr.slen,
621 (type & PJSIP_TRANSPORT_IPV6) ? "]" : "",
623 (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? ";transport=" : "",
624 (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? pjsip_transport_get_type_name(type) : "");
631 * \brief Check if a registration can be reused
633 * This checks if the existing outbound registration's configuration differs from a newly-applied
634 * outbound registration to see if the applied one.
636 * \param existing The pre-existing outbound registration
637 * \param applied The newly-created registration
639 static int can_reuse_registration(struct sip_outbound_registration *existing, struct sip_outbound_registration *applied)
643 if (strcmp(existing->server_uri, applied->server_uri) || strcmp(existing->client_uri, applied->client_uri) ||
644 strcmp(existing->transport, applied->transport) || strcmp(existing->contact_user, applied->contact_user) ||
645 strcmp(existing->outbound_proxy, applied->outbound_proxy) || existing->num_outbound_auths != applied->num_outbound_auths ||
646 existing->auth_rejection_permanent != applied->auth_rejection_permanent) {
650 for (i = 0; i < existing->num_outbound_auths; ++i) {
651 if (strcmp(existing->outbound_auths.names[i], applied->outbound_auths.names[i])) {
659 /*! \brief Helper function that allocates a pjsip registration client and configures it */
660 static int sip_outbound_registration_regc_alloc(void *data)
662 struct sip_outbound_registration *registration = data;
663 pj_str_t server_uri, client_uri, contact_uri;
664 pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
666 if (!ast_strlen_zero(registration->transport)) {
667 RAII_VAR(struct ast_sip_transport *, transport, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", registration->transport), ao2_cleanup);
669 if (!transport || !transport->state) {
670 ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport '%s' "
671 " for outbound registration", registration->transport);
675 if (transport->state->transport) {
676 selector.type = PJSIP_TPSELECTOR_TRANSPORT;
677 selector.u.transport = transport->state->transport;
678 } else if (transport->state->factory) {
679 selector.type = PJSIP_TPSELECTOR_LISTENER;
680 selector.u.listener = transport->state->factory;
686 pjsip_regc_set_transport(registration->state->client_state->client, &selector);
688 if (!ast_strlen_zero(registration->outbound_proxy)) {
689 pjsip_route_hdr route_set, *route;
690 static const pj_str_t ROUTE_HNAME = { "Route", 5 };
693 pj_list_init(&route_set);
695 pj_strdup2_with_null(pjsip_regc_get_pool(registration->state->client_state->client), &tmp, registration->outbound_proxy);
696 if (!(route = pjsip_parse_hdr(pjsip_regc_get_pool(registration->state->client_state->client), &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL))) {
699 pj_list_push_back(&route_set, route);
701 pjsip_regc_set_route_set(registration->state->client_state->client, &route_set);
704 pj_cstr(&server_uri, registration->server_uri);
706 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)) {
710 pj_cstr(&client_uri, registration->client_uri);
712 if (pjsip_regc_init(registration->state->client_state->client, &server_uri, &client_uri, &client_uri, 1, &contact_uri, registration->expiration) != PJ_SUCCESS) {
719 /*! \brief Apply function which finds or allocates a state structure */
720 static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, void *obj)
722 RAII_VAR(struct sip_outbound_registration *, existing, ast_sorcery_retrieve_by_id(sorcery, "registration", ast_sorcery_object_get_id(obj)), ao2_cleanup);
723 struct sip_outbound_registration *applied = obj;
726 /* If no existing registration exists we can just start fresh easily */
727 applied->state = sip_outbound_registration_state_alloc();
729 /* If there is an existing registration things are more complicated, we can immediately reuse this state if most stuff remains unchanged */
730 if (can_reuse_registration(existing, applied)) {
731 applied->state = existing->state;
732 ao2_ref(applied->state, +1);
735 applied->state = sip_outbound_registration_state_alloc();
738 if (!applied->state) {
742 return ast_sip_push_task_synchronous(NULL, sip_outbound_registration_regc_alloc, applied);
745 /*! \brief Helper function which performs a single registration */
746 static int sip_outbound_registration_perform(void *data)
748 RAII_VAR(struct sip_outbound_registration *, registration, data, ao2_cleanup);
751 /* Just in case the client state is being reused for this registration, free the auth information */
752 ast_sip_auth_array_destroy(®istration->state->client_state->outbound_auths);
754 registration->state->client_state->outbound_auths.names = ast_calloc(registration->outbound_auths.num, sizeof(char *));
755 for (i = 0; i < registration->outbound_auths.num; ++i) {
756 registration->state->client_state->outbound_auths.names[i] = ast_strdup(registration->outbound_auths.names[i]);
758 registration->state->client_state->outbound_auths.num = registration->outbound_auths.num;
759 registration->state->client_state->retry_interval = registration->retry_interval;
760 registration->state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval;
761 registration->state->client_state->max_retries = registration->max_retries;
762 registration->state->client_state->retries = 0;
764 pjsip_regc_update_expires(registration->state->client_state->client, registration->expiration);
766 schedule_registration(registration->state->client_state, (ast_random() % 10) + 1);
771 /*! \brief Helper function which performs all registrations */
772 static void sip_outbound_registration_perform_all(void)
774 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);
775 struct ao2_iterator i;
776 struct sip_outbound_registration *registration;
778 if (!registrations) {
782 i = ao2_iterator_init(registrations, 0);
783 while ((registration = ao2_iterator_next(&i))) {
784 if (ast_sip_push_task(registration->state->client_state->serializer, sip_outbound_registration_perform, registration)) {
785 ast_log(LOG_ERROR, "Failed to perform outbound registration on '%s'\n", ast_sorcery_object_get_id(registration));
786 ao2_ref(registration, -1);
789 ao2_iterator_destroy(&i);
792 static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
794 struct sip_outbound_registration *registration = obj;
796 return ast_sip_auth_array_init(®istration->outbound_auths, var->value);
799 static struct sip_outbound_registration *retrieve_registration(const char *registration_name)
801 return ast_sorcery_retrieve_by_id(
802 ast_sip_get_sorcery(),
807 static int unregister_task(void *obj)
809 RAII_VAR(struct sip_outbound_registration*, registration, obj, ao2_cleanup);
810 struct pjsip_regc *client = registration->state->client_state->client;
811 pjsip_tx_data *tdata;
813 if (pjsip_regc_unregister(client, &tdata) != PJ_SUCCESS) {
817 ao2_ref(registration->state->client_state, +1);
818 if (pjsip_regc_send(client, tdata) != PJ_SUCCESS) {
819 ao2_cleanup(registration->state->client_state);
825 static int queue_unregister(struct sip_outbound_registration *registration)
827 ao2_ref(registration, +1);
828 if (ast_sip_push_task(registration->state->client_state->serializer, unregister_task, registration)) {
829 ao2_cleanup(registration);
835 static char *cli_complete_registration(const char *line, const char *word,
841 struct sip_outbound_registration *registration;
842 RAII_VAR(struct ao2_container *, registrations, NULL, ao2_cleanup);
843 struct ao2_iterator i;
849 wordlen = strlen(word);
850 registrations = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration",
851 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
852 if (!registrations) {
856 i = ao2_iterator_init(registrations, 0);
857 while ((registration = ao2_iterator_next(&i))) {
858 const char *name = ast_sorcery_object_get_id(registration);
859 if (!strncasecmp(word, name, wordlen) && ++which > state) {
860 result = ast_strdup(name);
863 ao2_cleanup(registration);
868 ao2_iterator_destroy(&i);
872 static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
874 RAII_VAR(struct sip_outbound_registration *, registration, NULL, ao2_cleanup);
875 const char *registration_name;
879 e->command = "pjsip send unregister";
881 "Usage: pjsip send unregister <registration>\n"
882 " Send a SIP REGISTER request to the specified outbound "
883 "registration with an expiration of 0. This will cause the contact "
884 "added by this registration to be removed on the remote system.\n";
887 return cli_complete_registration(a->line, a->word, a->pos, a->n);
891 return CLI_SHOWUSAGE;
894 registration_name = a->argv[3];
896 registration = retrieve_registration(registration_name);
898 ast_cli(a->fd, "Unable to retrieve registration %s\n", registration_name);
902 if (queue_unregister(registration)) {
903 ast_cli(a->fd, "Failed to queue unregistration");
910 static int ami_unregister(struct mansession *s, const struct message *m)
912 const char *registration_name = astman_get_header(m, "Registration");
913 RAII_VAR(struct sip_outbound_registration *, registration, NULL, ao2_cleanup);
915 if (ast_strlen_zero(registration_name)) {
916 astman_send_error(s, m, "Registration parameter missing.");
920 registration = retrieve_registration(registration_name);
922 astman_send_error(s, m, "Unable to retrieve registration entry\n");
927 if (queue_unregister(registration)) {
928 astman_send_ack(s, m, "Failed to queue unregistration");
932 astman_send_ack(s, m, "Unregistration sent");
936 static struct ast_cli_entry cli_outbound_registration[] = {
937 AST_CLI_DEFINE(cli_unregister, "Send a REGISTER request to an outbound registration target with a expiration of 0")
940 static int load_module(void)
942 ast_sorcery_apply_default(ast_sip_get_sorcery(), "registration", "config", "pjsip.conf,criteria=type=registration");
944 if (ast_sorcery_object_register(ast_sip_get_sorcery(), "registration", sip_outbound_registration_alloc, NULL, sip_outbound_registration_apply)) {
945 return AST_MODULE_LOAD_DECLINE;
948 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "type", "", OPT_NOOP_T, 0, 0);
949 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "server_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, server_uri));
950 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "client_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, client_uri));
951 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_user));
952 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, transport));
953 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, outbound_proxy));
954 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, expiration));
955 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_interval", "60", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_interval));
956 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));
957 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "max_retries", "10", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, max_retries));
958 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));
959 ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "registration", "outbound_auth", "", outbound_auth_handler, NULL, 0, 0);
960 ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration");
961 sip_outbound_registration_perform_all();
963 ast_cli_register_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
964 ast_manager_register_xml("PJSIPUnregister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_unregister);
965 return AST_MODULE_LOAD_SUCCESS;
968 static int reload_module(void)
970 ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration");
971 sip_outbound_registration_perform_all();
975 static int unload_module(void)
977 ast_cli_unregister_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
978 ast_manager_unregister("PJSIPUnregister");
982 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Outbound Registration Support",
984 .reload = reload_module,
985 .unload = unload_module,
986 .load_pri = AST_MODPRI_APP_DEPEND,