2013-07-18 19:25:51 +00:00
|
|
|
/*
|
|
|
|
* Asterisk -- An open source telephony toolkit.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2013, Digium, Inc.
|
|
|
|
*
|
|
|
|
* Mark Michelson <mmichelson@digium.com>
|
|
|
|
*
|
|
|
|
* See http://www.asterisk.org for more information about
|
|
|
|
* the Asterisk project. Please do not directly contact
|
|
|
|
* any of the maintainers of this project for assistance;
|
|
|
|
* the project provides a web site, mailing lists and IRC
|
|
|
|
* channels for your use.
|
|
|
|
*
|
|
|
|
* This program is free software, distributed under the terms of
|
|
|
|
* the GNU General Public License Version 2. See the LICENSE file
|
|
|
|
* at the top of the source tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "asterisk.h"
|
|
|
|
|
|
|
|
#include <pjsip.h>
|
|
|
|
#include <pjlib.h>
|
|
|
|
|
2013-07-30 18:14:50 +00:00
|
|
|
#include "asterisk/res_pjsip.h"
|
2013-12-11 20:24:50 +00:00
|
|
|
#include "include/res_pjsip_private.h"
|
2019-01-15 23:20:30 +00:00
|
|
|
#include "asterisk/pbx.h"
|
2013-07-18 19:25:51 +00:00
|
|
|
#include "asterisk/sorcery.h"
|
2016-08-08 17:53:32 +00:00
|
|
|
#include "asterisk/taskprocessor.h"
|
2013-07-18 19:25:51 +00:00
|
|
|
#include "asterisk/ast_version.h"
|
2015-04-09 22:07:50 +00:00
|
|
|
#include "asterisk/res_pjsip_cli.h"
|
2013-07-18 19:25:51 +00:00
|
|
|
|
|
|
|
#define DEFAULT_MAX_FORWARDS 70
|
2018-07-20 11:54:35 +00:00
|
|
|
#define DEFAULT_KEEPALIVE_INTERVAL 90
|
2013-07-18 19:25:51 +00:00
|
|
|
#define DEFAULT_USERAGENT_PREFIX "Asterisk PBX"
|
2013-12-11 20:24:50 +00:00
|
|
|
#define DEFAULT_OUTBOUND_ENDPOINT "default_outbound_endpoint"
|
2015-03-11 15:26:32 +00:00
|
|
|
#define DEFAULT_DEBUG "no"
|
2015-03-17 18:22:20 +00:00
|
|
|
#define DEFAULT_ENDPOINT_IDENTIFIER_ORDER "ip,username,anonymous"
|
2015-04-11 22:04:32 +00:00
|
|
|
#define DEFAULT_MAX_INITIAL_QUALIFY_TIME 0
|
2015-09-04 19:40:38 +00:00
|
|
|
#define DEFAULT_FROM_USER "asterisk"
|
res_pjsip: Add ability to identify by Authorization username
A feature of chan_sip that service providers relied upon was the ability to
identify by the Authorization username. This is most often used when customers
have a PBX that needs to register rather than identify by IP address. From my
own experiance, this is pretty common with small businesses who otherwise
don't need a static IP.
In this scenario, a register from the customer's PBX may succeed because From
will usually contain the PBXs account id but an INVITE will contain the caller
id. With nothing recognizable in From, the service provider's Asterisk can
never match to an endpoint and the INVITE just stays unauthorized.
The fixes:
A new value "auth_username" has been added to endpoint/identify_by that
will use the username and digest fields in the Authorization header
instead of username and domain in the the From header to match an endpoint,
or the To header to match an aor. This code as added to
res_pjsip_endpoint_identifier_user rather than creating a new module.
Although identify_by was always a comma-separated list, there was only
1 choice so order wasn't preserved. So to keep the order, a vector was added
to the end of ast_sip_endpoint. This is only used by res_pjsip_registrar
to find the aor. The res_pjsip_endpoint_identifier_* modules are called in
globals/endpoint_identifier_order.
Along the way, the logic in res_pjsip_registrar was corrected to match
most-specific to least-specific as res_pjsip_endpoint_identifier_user does.
The order is:
username@domain
username@domain_alias
username
Auth by username does present 1 problem however, the first INVITE won't have
an Authorization header so the distributor, not finding a match on anything,
sends a securty_alert. It still sends a 401 with a challenge so the next
INVITE will have the Authorization header and presumably succeed. As a result
though, that first security alert is actually a false alarm.
To address this, a new feature has been added to pjsip_distributor that keeps
track of unidentified requests and only sends the security alert if a
configurable number of unidentified requests come from the same IP in a
configurable amout of time. Those configuration options have been added to
the global config object. This feature is only used when auth_username
is enabled.
Finally, default_realm was added to the globals object to replace the hard
coded "asterisk" used when an endpoint is not yet identified.
The testsuite tests all pass but new tests are forthcoming for this new
feature.
ASTERISK-25835 #close
Reported-by: Ross Beer
Change-Id: I30ba62d208e6f63439600916fcd1c08a365ed69d
2016-03-08 00:34:31 +00:00
|
|
|
#define DEFAULT_REALM "asterisk"
|
2016-01-10 22:22:12 +00:00
|
|
|
#define DEFAULT_REGCONTEXT ""
|
sorcery/res_pjsip: Refactor for realtime performance
There were a number of places in the res_pjsip stack that were getting
all endpoints or all aors, and then filtering them locally.
A good example is pjsip_options which, on startup, retrieves all
endpoints, then the aors for those endpoints, then tests the aors to see
if the qualify_frequency is > 0. One issue was that it never did
anything with the endpoints other than retrieve the aors so we probably
could have skipped a step and just retrieved all aors. But nevermind.
This worked reasonably well with local config files but with a realtime
backend and thousands of objects, this was a nightmare. The issue
really boiled down to the fact that while realtime supports predicates
that are passed to the database engine, the non-realtime sorcery
backends didn't.
They do now.
The realtime engines have a scheme for doing simple comparisons. They
take in an ast_variable (or list) for matching, and the name of each
variable can contain an operator. For instance, a name of
"qualify_frequency >" and a value of "0" would create a SQL predicate
that looks like "where qualify_frequency > '0'". If there's no operator
after the name, the engines add an '=' so a simple name of
"qualify_frequency" and a value of "10" would return exact matches.
The non-realtime backends decide whether to include an object in a
result set by calling ast_sorcery_changeset_create on every object in
the internal container. However, ast_sorcery_changeset_create only does
exact string matches though so a name of "qualify_frequency >" and a
value of "0" returns nothing because the literal "qualify_frequency >"
doesn't match any name in the objset set.
So, the real task was to create a generic string matcher that can take a
left value, operator and a right value and perform the match. To that
end, strings.c has a new ast_strings_match(left, operator, right)
function. Left and right are the strings to operate on and the operator
can be a string containing any of the following: = (or NULL or ""), !=,
>, >=, <, <=, like or regex. If the operator is like or regex, the
right string should be a %-pattern or a regex expression. If both left
and right can be converted to float, then a numeric comparison is
performed, otherwise a string comparison is performed.
To use this new function on ast_variables, 2 new functions were added to
config.c. One that compares 2 ast_variables, and one that compares 2
ast_variable lists. The former is useful when you want to compare 2
ast_variables that happen to be in a list but don't want to traverse the
list. The latter will traverse the right list and return true if all
the variables in it match the left list.
Now, the backends' fields_cmp functions call ast_variable_lists_match
instead of ast_sorcery_changeset_create and they can now process the
same syntax as the realtime engines. The realtime backend just passes
the variable list unaltered to the engine. The only gotcha is that
there's no common realtime engine support for regex so that's been noted
in the api docs for ast_sorcery_retrieve_by_fields.
Only one more change to sorcery was done... A new config flag
"allow_unqualified_fetch" was added to reg_sorcery_realtime.
"no": ignore fetches if no predicate fields were supplied.
"error": same as no but emit an error. (good for testing)
"yes": allow (the default);
"warn": allow but emit a warning. (good for testing)
Now on to res_pjsip...
pjsip_options was modified to retrieve aors with qualify_frequency > 0
rather than all endpoints then all aors. Not only was this a big
improvement in realtime retrieval but even for config files there's an
improvement because we're not going through endpoints anymore.
res_pjsip_mwi was modified to retieve only endpoints with something in
the mailboxes field instead of all endpoints then testing mailboxes.
res_pjsip_registrar_expire was completely refactored. It was retrieving
all contacts then setting up scheduler entries to check for expiration.
Now, it's a single thread (like keepalive) that periodically retrieves
only contacts whose expiration time is < now and deletes them. A new
contact_expiration_check_interval was added to global with a default of
30 seconds.
Ross Beer reports that with this patch, his Asterisk startup time dropped
from around an hour to under 30 seconds.
There are still objects that can't be filtered at the database like
identifies, transports, and registrations. These are not going to be
anywhere near as numerous as endpoints, aors, auths, contacts however.
Back to allow_unqualified_fetch. If this is set to yes and you have a
very large number of objects in the database, the pjsip CLI commands
will attempt to retrive ALL of them if not qualified with a LIKE.
Worse, if you type "pjsip show endpoint <tab>" guess what's going to
happen? :) Having a cache helps but all the objects will have to be
retrieved at least once to fill the cache. Setting
allow_unqualified_fetch=no prevents the mass retrieve and should be used
on endpoints, auths, aors, and contacts. It should NOT be used for
identifies, registrations and transports since these MUST be
retrieved in bulk.
Example sorcery.conf:
[res_pjsip]
endpoint=config,pjsip.conf,criteria=type=endpoint
endpoint=realtime,ps_endpoints,allow_unqualified_fetch=error
ASTERISK-25826 #close
Reported-by: Ross Beer
Tested-by: Ross Beer
Change-Id: Id2691e447db90892890036e663aaf907b2dc1c67
2016-03-08 21:55:30 +00:00
|
|
|
#define DEFAULT_CONTACT_EXPIRATION_CHECK_INTERVAL 30
|
2016-04-15 16:59:42 +00:00
|
|
|
#define DEFAULT_DISABLE_MULTI_DOMAIN 0
|
res_pjsip_mwi: Add voicemail extension and mwi_subscribe_replaces_unsolicited
res_pjsip_mwi was missing the chan_sip "vmexten" functionality which adds
the Message-Account header to the MWI NOTIFY. Also, specifying mailboxes
on endpoints for unsolicited mwi and on aors for subscriptions required
that the admin know in advance which the client wanted. If you specified
mailboxes on the endpoint, subscriptions were rejected even if you also
specified mailboxes on the aor.
Voicemail extension:
* Added a global default_voicemail_extension which defaults to "".
* Added voicemail_extension to both endpoint and aor.
* Added ast_sip_subscription_get_dialog for support.
* Added ast_sip_subscription_get_sip_uri for support.
When an unsolicited NOTIFY is constructed, the From header is parsed, the
voicemail extension from the endpoint is substituted for the user, and the
result placed in the Message-Account field in the body.
When a subscribed NOTIFY is constructed, the subscription dialog local uri
is parsed, the voicemail_extension from the aor (looked up from the
subscription resource name) is substituted for the user, and the result
placed in the Message-Account field in the body.
If no voicemail extension was defined, the Message-Account field is not added
to the NOTIFY body.
mwi_subscribe_replaces_unsolicited:
* Added mwi_subscribe_replaces_unsolicited to endpoint.
The previous behavior was to reject a subscribe if a previous internal
subscription for unsolicited MWI was found for the mailbox. That remains the
default. However, if there are mailboxes also set on the aor and the client
subscribes and mwi_subscribe_replaces_unsolicited is set, the existing internal
subscription is removed and replaced with the external subscription. This
allows an admin to configure mailboxes on both the endpoint and aor and allows
the client to select which to use.
ASTERISK-25865 #close
Reported-by: Ross Beer
Change-Id: Ic15a9415091760539c7134a5ba3dc4a6a1217cea
2016-03-25 03:55:03 +00:00
|
|
|
#define DEFAULT_VOICEMAIL_EXTENSION ""
|
res_pjsip: Add ability to identify by Authorization username
A feature of chan_sip that service providers relied upon was the ability to
identify by the Authorization username. This is most often used when customers
have a PBX that needs to register rather than identify by IP address. From my
own experiance, this is pretty common with small businesses who otherwise
don't need a static IP.
In this scenario, a register from the customer's PBX may succeed because From
will usually contain the PBXs account id but an INVITE will contain the caller
id. With nothing recognizable in From, the service provider's Asterisk can
never match to an endpoint and the INVITE just stays unauthorized.
The fixes:
A new value "auth_username" has been added to endpoint/identify_by that
will use the username and digest fields in the Authorization header
instead of username and domain in the the From header to match an endpoint,
or the To header to match an aor. This code as added to
res_pjsip_endpoint_identifier_user rather than creating a new module.
Although identify_by was always a comma-separated list, there was only
1 choice so order wasn't preserved. So to keep the order, a vector was added
to the end of ast_sip_endpoint. This is only used by res_pjsip_registrar
to find the aor. The res_pjsip_endpoint_identifier_* modules are called in
globals/endpoint_identifier_order.
Along the way, the logic in res_pjsip_registrar was corrected to match
most-specific to least-specific as res_pjsip_endpoint_identifier_user does.
The order is:
username@domain
username@domain_alias
username
Auth by username does present 1 problem however, the first INVITE won't have
an Authorization header so the distributor, not finding a match on anything,
sends a securty_alert. It still sends a 401 with a challenge so the next
INVITE will have the Authorization header and presumably succeed. As a result
though, that first security alert is actually a false alarm.
To address this, a new feature has been added to pjsip_distributor that keeps
track of unidentified requests and only sends the security alert if a
configurable number of unidentified requests come from the same IP in a
configurable amout of time. Those configuration options have been added to
the global config object. This feature is only used when auth_username
is enabled.
Finally, default_realm was added to the globals object to replace the hard
coded "asterisk" used when an endpoint is not yet identified.
The testsuite tests all pass but new tests are forthcoming for this new
feature.
ASTERISK-25835 #close
Reported-by: Ross Beer
Change-Id: I30ba62d208e6f63439600916fcd1c08a365ed69d
2016-03-08 00:34:31 +00:00
|
|
|
#define DEFAULT_UNIDENTIFIED_REQUEST_COUNT 5
|
|
|
|
#define DEFAULT_UNIDENTIFIED_REQUEST_PERIOD 5
|
|
|
|
#define DEFAULT_UNIDENTIFIED_REQUEST_PRUNE_INTERVAL 30
|
2016-08-08 17:53:32 +00:00
|
|
|
#define DEFAULT_MWI_TPS_QUEUE_HIGH AST_TASKPROCESSOR_HIGH_WATER_LEVEL
|
|
|
|
#define DEFAULT_MWI_TPS_QUEUE_LOW -1
|
|
|
|
#define DEFAULT_MWI_DISABLE_INITIAL_UNSOLICITED 0
|
2016-08-29 23:08:22 +00:00
|
|
|
#define DEFAULT_IGNORE_URI_USER_OPTIONS 0
|
2018-10-02 12:31:43 +00:00
|
|
|
#define DEFAULT_USE_CALLERID_CONTACT 0
|
2019-01-11 15:48:36 +00:00
|
|
|
#define DEFAULT_SEND_CONTACT_STATUS_ON_UPDATE_REGISTRATION 0
|
2019-02-15 18:53:50 +00:00
|
|
|
#define DEFAULT_TASKPROCESSOR_OVERLOAD_TRIGGER TASKPROCESSOR_OVERLOAD_TRIGGER_GLOBAL
|
2019-04-08 22:04:48 +00:00
|
|
|
#define DEFAULT_NOREFERSUB 1
|
2013-07-18 19:25:51 +00:00
|
|
|
|
2016-08-05 01:11:29 +00:00
|
|
|
/*!
|
|
|
|
* \brief Cached global config object
|
|
|
|
*
|
|
|
|
* \details
|
|
|
|
* Cached so we don't have to keep asking sorcery for the config.
|
|
|
|
* We could ask for it hundreds of times a second if not more.
|
|
|
|
*/
|
|
|
|
static AO2_GLOBAL_OBJ_STATIC(global_cfg);
|
|
|
|
|
2015-03-11 15:26:32 +00:00
|
|
|
static char default_useragent[256];
|
2013-07-18 19:25:51 +00:00
|
|
|
|
|
|
|
struct global_config {
|
|
|
|
SORCERY_OBJECT(details);
|
|
|
|
AST_DECLARE_STRING_FIELDS(
|
|
|
|
AST_STRING_FIELD(useragent);
|
2016-01-10 22:22:12 +00:00
|
|
|
AST_STRING_FIELD(regcontext);
|
2013-12-11 20:24:50 +00:00
|
|
|
AST_STRING_FIELD(default_outbound_endpoint);
|
2014-01-31 23:15:47 +00:00
|
|
|
/*! Debug logging yes|no|host */
|
|
|
|
AST_STRING_FIELD(debug);
|
2015-03-17 18:22:20 +00:00
|
|
|
/*! Order by which endpoint identifiers are checked (comma separated list) */
|
|
|
|
AST_STRING_FIELD(endpoint_identifier_order);
|
2015-09-04 19:40:38 +00:00
|
|
|
/*! User name to place in From header if there is no better option */
|
|
|
|
AST_STRING_FIELD(default_from_user);
|
res_pjsip_mwi: Add voicemail extension and mwi_subscribe_replaces_unsolicited
res_pjsip_mwi was missing the chan_sip "vmexten" functionality which adds
the Message-Account header to the MWI NOTIFY. Also, specifying mailboxes
on endpoints for unsolicited mwi and on aors for subscriptions required
that the admin know in advance which the client wanted. If you specified
mailboxes on the endpoint, subscriptions were rejected even if you also
specified mailboxes on the aor.
Voicemail extension:
* Added a global default_voicemail_extension which defaults to "".
* Added voicemail_extension to both endpoint and aor.
* Added ast_sip_subscription_get_dialog for support.
* Added ast_sip_subscription_get_sip_uri for support.
When an unsolicited NOTIFY is constructed, the From header is parsed, the
voicemail extension from the endpoint is substituted for the user, and the
result placed in the Message-Account field in the body.
When a subscribed NOTIFY is constructed, the subscription dialog local uri
is parsed, the voicemail_extension from the aor (looked up from the
subscription resource name) is substituted for the user, and the result
placed in the Message-Account field in the body.
If no voicemail extension was defined, the Message-Account field is not added
to the NOTIFY body.
mwi_subscribe_replaces_unsolicited:
* Added mwi_subscribe_replaces_unsolicited to endpoint.
The previous behavior was to reject a subscribe if a previous internal
subscription for unsolicited MWI was found for the mailbox. That remains the
default. However, if there are mailboxes also set on the aor and the client
subscribes and mwi_subscribe_replaces_unsolicited is set, the existing internal
subscription is removed and replaced with the external subscription. This
allows an admin to configure mailboxes on both the endpoint and aor and allows
the client to select which to use.
ASTERISK-25865 #close
Reported-by: Ross Beer
Change-Id: Ic15a9415091760539c7134a5ba3dc4a6a1217cea
2016-03-25 03:55:03 +00:00
|
|
|
/*! Default voicemail extension */
|
|
|
|
AST_STRING_FIELD(default_voicemail_extension);
|
res_pjsip: Add ability to identify by Authorization username
A feature of chan_sip that service providers relied upon was the ability to
identify by the Authorization username. This is most often used when customers
have a PBX that needs to register rather than identify by IP address. From my
own experiance, this is pretty common with small businesses who otherwise
don't need a static IP.
In this scenario, a register from the customer's PBX may succeed because From
will usually contain the PBXs account id but an INVITE will contain the caller
id. With nothing recognizable in From, the service provider's Asterisk can
never match to an endpoint and the INVITE just stays unauthorized.
The fixes:
A new value "auth_username" has been added to endpoint/identify_by that
will use the username and digest fields in the Authorization header
instead of username and domain in the the From header to match an endpoint,
or the To header to match an aor. This code as added to
res_pjsip_endpoint_identifier_user rather than creating a new module.
Although identify_by was always a comma-separated list, there was only
1 choice so order wasn't preserved. So to keep the order, a vector was added
to the end of ast_sip_endpoint. This is only used by res_pjsip_registrar
to find the aor. The res_pjsip_endpoint_identifier_* modules are called in
globals/endpoint_identifier_order.
Along the way, the logic in res_pjsip_registrar was corrected to match
most-specific to least-specific as res_pjsip_endpoint_identifier_user does.
The order is:
username@domain
username@domain_alias
username
Auth by username does present 1 problem however, the first INVITE won't have
an Authorization header so the distributor, not finding a match on anything,
sends a securty_alert. It still sends a 401 with a challenge so the next
INVITE will have the Authorization header and presumably succeed. As a result
though, that first security alert is actually a false alarm.
To address this, a new feature has been added to pjsip_distributor that keeps
track of unidentified requests and only sends the security alert if a
configurable number of unidentified requests come from the same IP in a
configurable amout of time. Those configuration options have been added to
the global config object. This feature is only used when auth_username
is enabled.
Finally, default_realm was added to the globals object to replace the hard
coded "asterisk" used when an endpoint is not yet identified.
The testsuite tests all pass but new tests are forthcoming for this new
feature.
ASTERISK-25835 #close
Reported-by: Ross Beer
Change-Id: I30ba62d208e6f63439600916fcd1c08a365ed69d
2016-03-08 00:34:31 +00:00
|
|
|
/*! Realm to use in challenges before an endpoint is identified */
|
|
|
|
AST_STRING_FIELD(default_realm);
|
2013-07-18 19:25:51 +00:00
|
|
|
);
|
2016-08-30 22:26:43 +00:00
|
|
|
/*! Value to put in Max-Forwards header */
|
2013-07-18 19:25:51 +00:00
|
|
|
unsigned int max_forwards;
|
2016-08-30 22:26:43 +00:00
|
|
|
/*! The interval at which to send keep alive messages to active connection-oriented transports */
|
2014-10-17 13:17:58 +00:00
|
|
|
unsigned int keep_alive_interval;
|
2016-08-30 22:26:43 +00:00
|
|
|
/*! The maximum time for all contacts to be qualified at startup */
|
2015-04-11 22:04:32 +00:00
|
|
|
unsigned int max_initial_qualify_time;
|
2016-08-30 22:26:43 +00:00
|
|
|
/*! The interval at which to check for expired contacts */
|
sorcery/res_pjsip: Refactor for realtime performance
There were a number of places in the res_pjsip stack that were getting
all endpoints or all aors, and then filtering them locally.
A good example is pjsip_options which, on startup, retrieves all
endpoints, then the aors for those endpoints, then tests the aors to see
if the qualify_frequency is > 0. One issue was that it never did
anything with the endpoints other than retrieve the aors so we probably
could have skipped a step and just retrieved all aors. But nevermind.
This worked reasonably well with local config files but with a realtime
backend and thousands of objects, this was a nightmare. The issue
really boiled down to the fact that while realtime supports predicates
that are passed to the database engine, the non-realtime sorcery
backends didn't.
They do now.
The realtime engines have a scheme for doing simple comparisons. They
take in an ast_variable (or list) for matching, and the name of each
variable can contain an operator. For instance, a name of
"qualify_frequency >" and a value of "0" would create a SQL predicate
that looks like "where qualify_frequency > '0'". If there's no operator
after the name, the engines add an '=' so a simple name of
"qualify_frequency" and a value of "10" would return exact matches.
The non-realtime backends decide whether to include an object in a
result set by calling ast_sorcery_changeset_create on every object in
the internal container. However, ast_sorcery_changeset_create only does
exact string matches though so a name of "qualify_frequency >" and a
value of "0" returns nothing because the literal "qualify_frequency >"
doesn't match any name in the objset set.
So, the real task was to create a generic string matcher that can take a
left value, operator and a right value and perform the match. To that
end, strings.c has a new ast_strings_match(left, operator, right)
function. Left and right are the strings to operate on and the operator
can be a string containing any of the following: = (or NULL or ""), !=,
>, >=, <, <=, like or regex. If the operator is like or regex, the
right string should be a %-pattern or a regex expression. If both left
and right can be converted to float, then a numeric comparison is
performed, otherwise a string comparison is performed.
To use this new function on ast_variables, 2 new functions were added to
config.c. One that compares 2 ast_variables, and one that compares 2
ast_variable lists. The former is useful when you want to compare 2
ast_variables that happen to be in a list but don't want to traverse the
list. The latter will traverse the right list and return true if all
the variables in it match the left list.
Now, the backends' fields_cmp functions call ast_variable_lists_match
instead of ast_sorcery_changeset_create and they can now process the
same syntax as the realtime engines. The realtime backend just passes
the variable list unaltered to the engine. The only gotcha is that
there's no common realtime engine support for regex so that's been noted
in the api docs for ast_sorcery_retrieve_by_fields.
Only one more change to sorcery was done... A new config flag
"allow_unqualified_fetch" was added to reg_sorcery_realtime.
"no": ignore fetches if no predicate fields were supplied.
"error": same as no but emit an error. (good for testing)
"yes": allow (the default);
"warn": allow but emit a warning. (good for testing)
Now on to res_pjsip...
pjsip_options was modified to retrieve aors with qualify_frequency > 0
rather than all endpoints then all aors. Not only was this a big
improvement in realtime retrieval but even for config files there's an
improvement because we're not going through endpoints anymore.
res_pjsip_mwi was modified to retieve only endpoints with something in
the mailboxes field instead of all endpoints then testing mailboxes.
res_pjsip_registrar_expire was completely refactored. It was retrieving
all contacts then setting up scheduler entries to check for expiration.
Now, it's a single thread (like keepalive) that periodically retrieves
only contacts whose expiration time is < now and deletes them. A new
contact_expiration_check_interval was added to global with a default of
30 seconds.
Ross Beer reports that with this patch, his Asterisk startup time dropped
from around an hour to under 30 seconds.
There are still objects that can't be filtered at the database like
identifies, transports, and registrations. These are not going to be
anywhere near as numerous as endpoints, aors, auths, contacts however.
Back to allow_unqualified_fetch. If this is set to yes and you have a
very large number of objects in the database, the pjsip CLI commands
will attempt to retrive ALL of them if not qualified with a LIKE.
Worse, if you type "pjsip show endpoint <tab>" guess what's going to
happen? :) Having a cache helps but all the objects will have to be
retrieved at least once to fill the cache. Setting
allow_unqualified_fetch=no prevents the mass retrieve and should be used
on endpoints, auths, aors, and contacts. It should NOT be used for
identifies, registrations and transports since these MUST be
retrieved in bulk.
Example sorcery.conf:
[res_pjsip]
endpoint=config,pjsip.conf,criteria=type=endpoint
endpoint=realtime,ps_endpoints,allow_unqualified_fetch=error
ASTERISK-25826 #close
Reported-by: Ross Beer
Tested-by: Ross Beer
Change-Id: Id2691e447db90892890036e663aaf907b2dc1c67
2016-03-08 21:55:30 +00:00
|
|
|
unsigned int contact_expiration_check_interval;
|
2016-04-15 16:59:42 +00:00
|
|
|
/*! Nonzero to disable multi domain support */
|
|
|
|
unsigned int disable_multi_domain;
|
2016-08-30 22:26:43 +00:00
|
|
|
/*! The maximum number of unidentified requests per source IP address before a security event is logged */
|
res_pjsip: Add ability to identify by Authorization username
A feature of chan_sip that service providers relied upon was the ability to
identify by the Authorization username. This is most often used when customers
have a PBX that needs to register rather than identify by IP address. From my
own experiance, this is pretty common with small businesses who otherwise
don't need a static IP.
In this scenario, a register from the customer's PBX may succeed because From
will usually contain the PBXs account id but an INVITE will contain the caller
id. With nothing recognizable in From, the service provider's Asterisk can
never match to an endpoint and the INVITE just stays unauthorized.
The fixes:
A new value "auth_username" has been added to endpoint/identify_by that
will use the username and digest fields in the Authorization header
instead of username and domain in the the From header to match an endpoint,
or the To header to match an aor. This code as added to
res_pjsip_endpoint_identifier_user rather than creating a new module.
Although identify_by was always a comma-separated list, there was only
1 choice so order wasn't preserved. So to keep the order, a vector was added
to the end of ast_sip_endpoint. This is only used by res_pjsip_registrar
to find the aor. The res_pjsip_endpoint_identifier_* modules are called in
globals/endpoint_identifier_order.
Along the way, the logic in res_pjsip_registrar was corrected to match
most-specific to least-specific as res_pjsip_endpoint_identifier_user does.
The order is:
username@domain
username@domain_alias
username
Auth by username does present 1 problem however, the first INVITE won't have
an Authorization header so the distributor, not finding a match on anything,
sends a securty_alert. It still sends a 401 with a challenge so the next
INVITE will have the Authorization header and presumably succeed. As a result
though, that first security alert is actually a false alarm.
To address this, a new feature has been added to pjsip_distributor that keeps
track of unidentified requests and only sends the security alert if a
configurable number of unidentified requests come from the same IP in a
configurable amout of time. Those configuration options have been added to
the global config object. This feature is only used when auth_username
is enabled.
Finally, default_realm was added to the globals object to replace the hard
coded "asterisk" used when an endpoint is not yet identified.
The testsuite tests all pass but new tests are forthcoming for this new
feature.
ASTERISK-25835 #close
Reported-by: Ross Beer
Change-Id: I30ba62d208e6f63439600916fcd1c08a365ed69d
2016-03-08 00:34:31 +00:00
|
|
|
unsigned int unidentified_request_count;
|
2016-08-30 22:26:43 +00:00
|
|
|
/*! The period during which unidentified requests are accumulated */
|
res_pjsip: Add ability to identify by Authorization username
A feature of chan_sip that service providers relied upon was the ability to
identify by the Authorization username. This is most often used when customers
have a PBX that needs to register rather than identify by IP address. From my
own experiance, this is pretty common with small businesses who otherwise
don't need a static IP.
In this scenario, a register from the customer's PBX may succeed because From
will usually contain the PBXs account id but an INVITE will contain the caller
id. With nothing recognizable in From, the service provider's Asterisk can
never match to an endpoint and the INVITE just stays unauthorized.
The fixes:
A new value "auth_username" has been added to endpoint/identify_by that
will use the username and digest fields in the Authorization header
instead of username and domain in the the From header to match an endpoint,
or the To header to match an aor. This code as added to
res_pjsip_endpoint_identifier_user rather than creating a new module.
Although identify_by was always a comma-separated list, there was only
1 choice so order wasn't preserved. So to keep the order, a vector was added
to the end of ast_sip_endpoint. This is only used by res_pjsip_registrar
to find the aor. The res_pjsip_endpoint_identifier_* modules are called in
globals/endpoint_identifier_order.
Along the way, the logic in res_pjsip_registrar was corrected to match
most-specific to least-specific as res_pjsip_endpoint_identifier_user does.
The order is:
username@domain
username@domain_alias
username
Auth by username does present 1 problem however, the first INVITE won't have
an Authorization header so the distributor, not finding a match on anything,
sends a securty_alert. It still sends a 401 with a challenge so the next
INVITE will have the Authorization header and presumably succeed. As a result
though, that first security alert is actually a false alarm.
To address this, a new feature has been added to pjsip_distributor that keeps
track of unidentified requests and only sends the security alert if a
configurable number of unidentified requests come from the same IP in a
configurable amout of time. Those configuration options have been added to
the global config object. This feature is only used when auth_username
is enabled.
Finally, default_realm was added to the globals object to replace the hard
coded "asterisk" used when an endpoint is not yet identified.
The testsuite tests all pass but new tests are forthcoming for this new
feature.
ASTERISK-25835 #close
Reported-by: Ross Beer
Change-Id: I30ba62d208e6f63439600916fcd1c08a365ed69d
2016-03-08 00:34:31 +00:00
|
|
|
unsigned int unidentified_request_period;
|
2016-08-30 22:26:43 +00:00
|
|
|
/*! Interval at which expired unidentifed requests will be pruned */
|
res_pjsip: Add ability to identify by Authorization username
A feature of chan_sip that service providers relied upon was the ability to
identify by the Authorization username. This is most often used when customers
have a PBX that needs to register rather than identify by IP address. From my
own experiance, this is pretty common with small businesses who otherwise
don't need a static IP.
In this scenario, a register from the customer's PBX may succeed because From
will usually contain the PBXs account id but an INVITE will contain the caller
id. With nothing recognizable in From, the service provider's Asterisk can
never match to an endpoint and the INVITE just stays unauthorized.
The fixes:
A new value "auth_username" has been added to endpoint/identify_by that
will use the username and digest fields in the Authorization header
instead of username and domain in the the From header to match an endpoint,
or the To header to match an aor. This code as added to
res_pjsip_endpoint_identifier_user rather than creating a new module.
Although identify_by was always a comma-separated list, there was only
1 choice so order wasn't preserved. So to keep the order, a vector was added
to the end of ast_sip_endpoint. This is only used by res_pjsip_registrar
to find the aor. The res_pjsip_endpoint_identifier_* modules are called in
globals/endpoint_identifier_order.
Along the way, the logic in res_pjsip_registrar was corrected to match
most-specific to least-specific as res_pjsip_endpoint_identifier_user does.
The order is:
username@domain
username@domain_alias
username
Auth by username does present 1 problem however, the first INVITE won't have
an Authorization header so the distributor, not finding a match on anything,
sends a securty_alert. It still sends a 401 with a challenge so the next
INVITE will have the Authorization header and presumably succeed. As a result
though, that first security alert is actually a false alarm.
To address this, a new feature has been added to pjsip_distributor that keeps
track of unidentified requests and only sends the security alert if a
configurable number of unidentified requests come from the same IP in a
configurable amout of time. Those configuration options have been added to
the global config object. This feature is only used when auth_username
is enabled.
Finally, default_realm was added to the globals object to replace the hard
coded "asterisk" used when an endpoint is not yet identified.
The testsuite tests all pass but new tests are forthcoming for this new
feature.
ASTERISK-25835 #close
Reported-by: Ross Beer
Change-Id: I30ba62d208e6f63439600916fcd1c08a365ed69d
2016-03-08 00:34:31 +00:00
|
|
|
unsigned int unidentified_request_prune_interval;
|
2016-08-08 17:53:32 +00:00
|
|
|
struct {
|
|
|
|
/*! Taskprocessor high water alert trigger level */
|
|
|
|
unsigned int tps_queue_high;
|
|
|
|
/*! Taskprocessor low water clear alert level. */
|
|
|
|
int tps_queue_low;
|
|
|
|
/*! Nonzero to disable sending unsolicited mwi to all endpoints on startup */
|
|
|
|
unsigned int disable_initial_unsolicited;
|
|
|
|
} mwi;
|
2016-08-29 23:08:22 +00:00
|
|
|
/*! Nonzero if URI user field options are ignored. */
|
|
|
|
unsigned int ignore_uri_user_options;
|
2018-10-02 12:31:43 +00:00
|
|
|
/*! Nonzero if CALLERID(num) is to be used as the default contact username instead of default_from_user */
|
|
|
|
unsigned int use_callerid_contact;
|
2019-01-11 15:48:36 +00:00
|
|
|
/*! Nonzero if need to send AMI ContactStatus event when a contact is updated */
|
|
|
|
unsigned int send_contact_status_on_update_registration;
|
2019-02-15 18:53:50 +00:00
|
|
|
/*! Trigger the distributor should use to pause accepting new dialogs */
|
|
|
|
enum ast_sip_taskprocessor_overload_trigger overload_trigger;
|
2019-04-08 22:04:48 +00:00
|
|
|
/*! Nonzero if norefersub is to be sent in Supported header */
|
|
|
|
unsigned int norefersub;
|
2013-07-18 19:25:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void global_destructor(void *obj)
|
|
|
|
{
|
|
|
|
struct global_config *cfg = obj;
|
|
|
|
|
|
|
|
ast_string_field_free_memory(cfg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *global_alloc(const char *name)
|
|
|
|
{
|
2015-03-11 15:26:32 +00:00
|
|
|
struct global_config *cfg;
|
2013-07-18 19:25:51 +00:00
|
|
|
|
2015-03-11 15:26:32 +00:00
|
|
|
cfg = ast_sorcery_generic_alloc(sizeof(*cfg), global_destructor);
|
2015-03-09 16:13:40 +00:00
|
|
|
if (!cfg || ast_string_field_init(cfg, 100)) {
|
2015-03-11 15:26:32 +00:00
|
|
|
ao2_cleanup(cfg);
|
2013-07-18 19:25:51 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cfg;
|
|
|
|
}
|
|
|
|
|
2019-01-15 23:20:30 +00:00
|
|
|
/*
|
|
|
|
* There is ever only one global section, so we can use a single global
|
|
|
|
* value here to track the regcontext through reloads.
|
|
|
|
*/
|
|
|
|
static char *previous_regcontext = NULL;
|
|
|
|
|
|
|
|
static int check_regcontext(const struct global_config *cfg)
|
|
|
|
{
|
|
|
|
char *current = NULL;
|
|
|
|
|
|
|
|
if (previous_regcontext && !strcmp(previous_regcontext, cfg->regcontext)) {
|
|
|
|
/* Nothing changed so nothing to do */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ast_strlen_zero(cfg->regcontext)) {
|
|
|
|
current = ast_strdup(cfg->regcontext);
|
|
|
|
if (!current) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ast_sip_persistent_endpoint_add_to_regcontext(cfg->regcontext)) {
|
|
|
|
ast_free(current);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ast_strlen_zero(previous_regcontext)) {
|
|
|
|
ast_context_destroy_by_name(previous_regcontext, "PJSIP");
|
|
|
|
ast_free(previous_regcontext);
|
|
|
|
previous_regcontext = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (current) {
|
|
|
|
previous_regcontext = current;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-07-18 19:25:51 +00:00
|
|
|
static int global_apply(const struct ast_sorcery *sorcery, void *obj)
|
|
|
|
{
|
|
|
|
struct global_config *cfg = obj;
|
|
|
|
char max_forwards[10];
|
|
|
|
|
2016-08-11 17:18:15 +00:00
|
|
|
if (ast_strlen_zero(cfg->debug)) {
|
|
|
|
ast_log(LOG_ERROR,
|
|
|
|
"Global option 'debug' can't be empty. Set it to a valid value or remove the entry to accept 'no' as the default\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ast_strlen_zero(cfg->default_from_user)) {
|
|
|
|
ast_log(LOG_ERROR,
|
|
|
|
"Global option 'default_from_user' can't be empty. Set it to a valid value or remove the entry to accept 'asterisk' as the default\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-07-18 19:25:51 +00:00
|
|
|
snprintf(max_forwards, sizeof(max_forwards), "%u", cfg->max_forwards);
|
|
|
|
|
|
|
|
ast_sip_add_global_request_header("Max-Forwards", max_forwards, 1);
|
|
|
|
ast_sip_add_global_request_header("User-Agent", cfg->useragent, 1);
|
|
|
|
ast_sip_add_global_response_header("Server", cfg->useragent, 1);
|
2016-08-05 01:11:29 +00:00
|
|
|
|
2019-01-15 23:20:30 +00:00
|
|
|
if (check_regcontext(cfg)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-08-05 01:11:29 +00:00
|
|
|
ao2_t_global_obj_replace_unref(global_cfg, cfg, "Applying global settings");
|
2013-07-18 19:25:51 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-11 20:24:50 +00:00
|
|
|
static struct global_config *get_global_cfg(void)
|
|
|
|
{
|
2016-08-05 01:11:29 +00:00
|
|
|
return ao2_global_obj_ref(global_cfg);
|
2013-12-11 20:24:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char *ast_sip_global_default_outbound_endpoint(void)
|
|
|
|
{
|
2015-03-11 15:26:32 +00:00
|
|
|
char *str;
|
|
|
|
struct global_config *cfg;
|
2013-12-11 20:24:50 +00:00
|
|
|
|
2015-03-11 15:26:32 +00:00
|
|
|
cfg = get_global_cfg();
|
2013-12-11 20:24:50 +00:00
|
|
|
if (!cfg) {
|
2015-03-11 15:26:32 +00:00
|
|
|
return ast_strdup(DEFAULT_OUTBOUND_ENDPOINT);
|
2013-12-11 20:24:50 +00:00
|
|
|
}
|
|
|
|
|
2015-03-11 15:26:32 +00:00
|
|
|
str = ast_strdup(cfg->default_outbound_endpoint);
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return str;
|
2013-12-11 20:24:50 +00:00
|
|
|
}
|
|
|
|
|
2014-01-31 23:15:47 +00:00
|
|
|
char *ast_sip_get_debug(void)
|
|
|
|
{
|
|
|
|
char *res;
|
2015-03-11 15:26:32 +00:00
|
|
|
struct global_config *cfg;
|
2014-01-31 23:15:47 +00:00
|
|
|
|
2015-03-11 15:26:32 +00:00
|
|
|
cfg = get_global_cfg();
|
2014-01-31 23:15:47 +00:00
|
|
|
if (!cfg) {
|
2015-03-11 15:26:32 +00:00
|
|
|
return ast_strdup(DEFAULT_DEBUG);
|
2014-01-31 23:15:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
res = ast_strdup(cfg->debug);
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2016-01-10 22:22:12 +00:00
|
|
|
char *ast_sip_get_regcontext(void)
|
|
|
|
{
|
res_pjsip_mwi: Add voicemail extension and mwi_subscribe_replaces_unsolicited
res_pjsip_mwi was missing the chan_sip "vmexten" functionality which adds
the Message-Account header to the MWI NOTIFY. Also, specifying mailboxes
on endpoints for unsolicited mwi and on aors for subscriptions required
that the admin know in advance which the client wanted. If you specified
mailboxes on the endpoint, subscriptions were rejected even if you also
specified mailboxes on the aor.
Voicemail extension:
* Added a global default_voicemail_extension which defaults to "".
* Added voicemail_extension to both endpoint and aor.
* Added ast_sip_subscription_get_dialog for support.
* Added ast_sip_subscription_get_sip_uri for support.
When an unsolicited NOTIFY is constructed, the From header is parsed, the
voicemail extension from the endpoint is substituted for the user, and the
result placed in the Message-Account field in the body.
When a subscribed NOTIFY is constructed, the subscription dialog local uri
is parsed, the voicemail_extension from the aor (looked up from the
subscription resource name) is substituted for the user, and the result
placed in the Message-Account field in the body.
If no voicemail extension was defined, the Message-Account field is not added
to the NOTIFY body.
mwi_subscribe_replaces_unsolicited:
* Added mwi_subscribe_replaces_unsolicited to endpoint.
The previous behavior was to reject a subscribe if a previous internal
subscription for unsolicited MWI was found for the mailbox. That remains the
default. However, if there are mailboxes also set on the aor and the client
subscribes and mwi_subscribe_replaces_unsolicited is set, the existing internal
subscription is removed and replaced with the external subscription. This
allows an admin to configure mailboxes on both the endpoint and aor and allows
the client to select which to use.
ASTERISK-25865 #close
Reported-by: Ross Beer
Change-Id: Ic15a9415091760539c7134a5ba3dc4a6a1217cea
2016-03-25 03:55:03 +00:00
|
|
|
char *res;
|
|
|
|
struct global_config *cfg;
|
2016-01-10 22:22:12 +00:00
|
|
|
|
res_pjsip_mwi: Add voicemail extension and mwi_subscribe_replaces_unsolicited
res_pjsip_mwi was missing the chan_sip "vmexten" functionality which adds
the Message-Account header to the MWI NOTIFY. Also, specifying mailboxes
on endpoints for unsolicited mwi and on aors for subscriptions required
that the admin know in advance which the client wanted. If you specified
mailboxes on the endpoint, subscriptions were rejected even if you also
specified mailboxes on the aor.
Voicemail extension:
* Added a global default_voicemail_extension which defaults to "".
* Added voicemail_extension to both endpoint and aor.
* Added ast_sip_subscription_get_dialog for support.
* Added ast_sip_subscription_get_sip_uri for support.
When an unsolicited NOTIFY is constructed, the From header is parsed, the
voicemail extension from the endpoint is substituted for the user, and the
result placed in the Message-Account field in the body.
When a subscribed NOTIFY is constructed, the subscription dialog local uri
is parsed, the voicemail_extension from the aor (looked up from the
subscription resource name) is substituted for the user, and the result
placed in the Message-Account field in the body.
If no voicemail extension was defined, the Message-Account field is not added
to the NOTIFY body.
mwi_subscribe_replaces_unsolicited:
* Added mwi_subscribe_replaces_unsolicited to endpoint.
The previous behavior was to reject a subscribe if a previous internal
subscription for unsolicited MWI was found for the mailbox. That remains the
default. However, if there are mailboxes also set on the aor and the client
subscribes and mwi_subscribe_replaces_unsolicited is set, the existing internal
subscription is removed and replaced with the external subscription. This
allows an admin to configure mailboxes on both the endpoint and aor and allows
the client to select which to use.
ASTERISK-25865 #close
Reported-by: Ross Beer
Change-Id: Ic15a9415091760539c7134a5ba3dc4a6a1217cea
2016-03-25 03:55:03 +00:00
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
|
|
|
return ast_strdup(DEFAULT_REGCONTEXT);
|
|
|
|
}
|
2016-01-10 22:22:12 +00:00
|
|
|
|
res_pjsip_mwi: Add voicemail extension and mwi_subscribe_replaces_unsolicited
res_pjsip_mwi was missing the chan_sip "vmexten" functionality which adds
the Message-Account header to the MWI NOTIFY. Also, specifying mailboxes
on endpoints for unsolicited mwi and on aors for subscriptions required
that the admin know in advance which the client wanted. If you specified
mailboxes on the endpoint, subscriptions were rejected even if you also
specified mailboxes on the aor.
Voicemail extension:
* Added a global default_voicemail_extension which defaults to "".
* Added voicemail_extension to both endpoint and aor.
* Added ast_sip_subscription_get_dialog for support.
* Added ast_sip_subscription_get_sip_uri for support.
When an unsolicited NOTIFY is constructed, the From header is parsed, the
voicemail extension from the endpoint is substituted for the user, and the
result placed in the Message-Account field in the body.
When a subscribed NOTIFY is constructed, the subscription dialog local uri
is parsed, the voicemail_extension from the aor (looked up from the
subscription resource name) is substituted for the user, and the result
placed in the Message-Account field in the body.
If no voicemail extension was defined, the Message-Account field is not added
to the NOTIFY body.
mwi_subscribe_replaces_unsolicited:
* Added mwi_subscribe_replaces_unsolicited to endpoint.
The previous behavior was to reject a subscribe if a previous internal
subscription for unsolicited MWI was found for the mailbox. That remains the
default. However, if there are mailboxes also set on the aor and the client
subscribes and mwi_subscribe_replaces_unsolicited is set, the existing internal
subscription is removed and replaced with the external subscription. This
allows an admin to configure mailboxes on both the endpoint and aor and allows
the client to select which to use.
ASTERISK-25865 #close
Reported-by: Ross Beer
Change-Id: Ic15a9415091760539c7134a5ba3dc4a6a1217cea
2016-03-25 03:55:03 +00:00
|
|
|
res = ast_strdup(cfg->regcontext);
|
|
|
|
ao2_ref(cfg, -1);
|
2016-01-10 22:22:12 +00:00
|
|
|
|
res_pjsip_mwi: Add voicemail extension and mwi_subscribe_replaces_unsolicited
res_pjsip_mwi was missing the chan_sip "vmexten" functionality which adds
the Message-Account header to the MWI NOTIFY. Also, specifying mailboxes
on endpoints for unsolicited mwi and on aors for subscriptions required
that the admin know in advance which the client wanted. If you specified
mailboxes on the endpoint, subscriptions were rejected even if you also
specified mailboxes on the aor.
Voicemail extension:
* Added a global default_voicemail_extension which defaults to "".
* Added voicemail_extension to both endpoint and aor.
* Added ast_sip_subscription_get_dialog for support.
* Added ast_sip_subscription_get_sip_uri for support.
When an unsolicited NOTIFY is constructed, the From header is parsed, the
voicemail extension from the endpoint is substituted for the user, and the
result placed in the Message-Account field in the body.
When a subscribed NOTIFY is constructed, the subscription dialog local uri
is parsed, the voicemail_extension from the aor (looked up from the
subscription resource name) is substituted for the user, and the result
placed in the Message-Account field in the body.
If no voicemail extension was defined, the Message-Account field is not added
to the NOTIFY body.
mwi_subscribe_replaces_unsolicited:
* Added mwi_subscribe_replaces_unsolicited to endpoint.
The previous behavior was to reject a subscribe if a previous internal
subscription for unsolicited MWI was found for the mailbox. That remains the
default. However, if there are mailboxes also set on the aor and the client
subscribes and mwi_subscribe_replaces_unsolicited is set, the existing internal
subscription is removed and replaced with the external subscription. This
allows an admin to configure mailboxes on both the endpoint and aor and allows
the client to select which to use.
ASTERISK-25865 #close
Reported-by: Ross Beer
Change-Id: Ic15a9415091760539c7134a5ba3dc4a6a1217cea
2016-03-25 03:55:03 +00:00
|
|
|
return res;
|
2016-01-10 22:22:12 +00:00
|
|
|
}
|
|
|
|
|
res_pjsip_mwi: Add voicemail extension and mwi_subscribe_replaces_unsolicited
res_pjsip_mwi was missing the chan_sip "vmexten" functionality which adds
the Message-Account header to the MWI NOTIFY. Also, specifying mailboxes
on endpoints for unsolicited mwi and on aors for subscriptions required
that the admin know in advance which the client wanted. If you specified
mailboxes on the endpoint, subscriptions were rejected even if you also
specified mailboxes on the aor.
Voicemail extension:
* Added a global default_voicemail_extension which defaults to "".
* Added voicemail_extension to both endpoint and aor.
* Added ast_sip_subscription_get_dialog for support.
* Added ast_sip_subscription_get_sip_uri for support.
When an unsolicited NOTIFY is constructed, the From header is parsed, the
voicemail extension from the endpoint is substituted for the user, and the
result placed in the Message-Account field in the body.
When a subscribed NOTIFY is constructed, the subscription dialog local uri
is parsed, the voicemail_extension from the aor (looked up from the
subscription resource name) is substituted for the user, and the result
placed in the Message-Account field in the body.
If no voicemail extension was defined, the Message-Account field is not added
to the NOTIFY body.
mwi_subscribe_replaces_unsolicited:
* Added mwi_subscribe_replaces_unsolicited to endpoint.
The previous behavior was to reject a subscribe if a previous internal
subscription for unsolicited MWI was found for the mailbox. That remains the
default. However, if there are mailboxes also set on the aor and the client
subscribes and mwi_subscribe_replaces_unsolicited is set, the existing internal
subscription is removed and replaced with the external subscription. This
allows an admin to configure mailboxes on both the endpoint and aor and allows
the client to select which to use.
ASTERISK-25865 #close
Reported-by: Ross Beer
Change-Id: Ic15a9415091760539c7134a5ba3dc4a6a1217cea
2016-03-25 03:55:03 +00:00
|
|
|
char *ast_sip_get_default_voicemail_extension(void)
|
|
|
|
{
|
|
|
|
char *res;
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
|
|
|
return ast_strdup(DEFAULT_VOICEMAIL_EXTENSION);
|
|
|
|
}
|
|
|
|
|
|
|
|
res = ast_strdup(cfg->default_voicemail_extension);
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
2016-01-10 22:22:12 +00:00
|
|
|
|
2015-03-17 18:22:20 +00:00
|
|
|
char *ast_sip_get_endpoint_identifier_order(void)
|
|
|
|
{
|
|
|
|
char *res;
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
|
|
|
return ast_strdup(DEFAULT_ENDPOINT_IDENTIFIER_ORDER);
|
|
|
|
}
|
|
|
|
|
|
|
|
res = ast_strdup(cfg->endpoint_identifier_order);
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2014-10-17 13:17:58 +00:00
|
|
|
unsigned int ast_sip_get_keep_alive_interval(void)
|
|
|
|
{
|
|
|
|
unsigned int interval;
|
2015-03-11 15:26:32 +00:00
|
|
|
struct global_config *cfg;
|
2014-10-17 13:17:58 +00:00
|
|
|
|
2015-03-11 15:26:32 +00:00
|
|
|
cfg = get_global_cfg();
|
2014-10-17 13:17:58 +00:00
|
|
|
if (!cfg) {
|
2015-03-11 15:26:32 +00:00
|
|
|
return DEFAULT_KEEPALIVE_INTERVAL;
|
2014-10-17 13:17:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
interval = cfg->keep_alive_interval;
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return interval;
|
|
|
|
}
|
|
|
|
|
sorcery/res_pjsip: Refactor for realtime performance
There were a number of places in the res_pjsip stack that were getting
all endpoints or all aors, and then filtering them locally.
A good example is pjsip_options which, on startup, retrieves all
endpoints, then the aors for those endpoints, then tests the aors to see
if the qualify_frequency is > 0. One issue was that it never did
anything with the endpoints other than retrieve the aors so we probably
could have skipped a step and just retrieved all aors. But nevermind.
This worked reasonably well with local config files but with a realtime
backend and thousands of objects, this was a nightmare. The issue
really boiled down to the fact that while realtime supports predicates
that are passed to the database engine, the non-realtime sorcery
backends didn't.
They do now.
The realtime engines have a scheme for doing simple comparisons. They
take in an ast_variable (or list) for matching, and the name of each
variable can contain an operator. For instance, a name of
"qualify_frequency >" and a value of "0" would create a SQL predicate
that looks like "where qualify_frequency > '0'". If there's no operator
after the name, the engines add an '=' so a simple name of
"qualify_frequency" and a value of "10" would return exact matches.
The non-realtime backends decide whether to include an object in a
result set by calling ast_sorcery_changeset_create on every object in
the internal container. However, ast_sorcery_changeset_create only does
exact string matches though so a name of "qualify_frequency >" and a
value of "0" returns nothing because the literal "qualify_frequency >"
doesn't match any name in the objset set.
So, the real task was to create a generic string matcher that can take a
left value, operator and a right value and perform the match. To that
end, strings.c has a new ast_strings_match(left, operator, right)
function. Left and right are the strings to operate on and the operator
can be a string containing any of the following: = (or NULL or ""), !=,
>, >=, <, <=, like or regex. If the operator is like or regex, the
right string should be a %-pattern or a regex expression. If both left
and right can be converted to float, then a numeric comparison is
performed, otherwise a string comparison is performed.
To use this new function on ast_variables, 2 new functions were added to
config.c. One that compares 2 ast_variables, and one that compares 2
ast_variable lists. The former is useful when you want to compare 2
ast_variables that happen to be in a list but don't want to traverse the
list. The latter will traverse the right list and return true if all
the variables in it match the left list.
Now, the backends' fields_cmp functions call ast_variable_lists_match
instead of ast_sorcery_changeset_create and they can now process the
same syntax as the realtime engines. The realtime backend just passes
the variable list unaltered to the engine. The only gotcha is that
there's no common realtime engine support for regex so that's been noted
in the api docs for ast_sorcery_retrieve_by_fields.
Only one more change to sorcery was done... A new config flag
"allow_unqualified_fetch" was added to reg_sorcery_realtime.
"no": ignore fetches if no predicate fields were supplied.
"error": same as no but emit an error. (good for testing)
"yes": allow (the default);
"warn": allow but emit a warning. (good for testing)
Now on to res_pjsip...
pjsip_options was modified to retrieve aors with qualify_frequency > 0
rather than all endpoints then all aors. Not only was this a big
improvement in realtime retrieval but even for config files there's an
improvement because we're not going through endpoints anymore.
res_pjsip_mwi was modified to retieve only endpoints with something in
the mailboxes field instead of all endpoints then testing mailboxes.
res_pjsip_registrar_expire was completely refactored. It was retrieving
all contacts then setting up scheduler entries to check for expiration.
Now, it's a single thread (like keepalive) that periodically retrieves
only contacts whose expiration time is < now and deletes them. A new
contact_expiration_check_interval was added to global with a default of
30 seconds.
Ross Beer reports that with this patch, his Asterisk startup time dropped
from around an hour to under 30 seconds.
There are still objects that can't be filtered at the database like
identifies, transports, and registrations. These are not going to be
anywhere near as numerous as endpoints, aors, auths, contacts however.
Back to allow_unqualified_fetch. If this is set to yes and you have a
very large number of objects in the database, the pjsip CLI commands
will attempt to retrive ALL of them if not qualified with a LIKE.
Worse, if you type "pjsip show endpoint <tab>" guess what's going to
happen? :) Having a cache helps but all the objects will have to be
retrieved at least once to fill the cache. Setting
allow_unqualified_fetch=no prevents the mass retrieve and should be used
on endpoints, auths, aors, and contacts. It should NOT be used for
identifies, registrations and transports since these MUST be
retrieved in bulk.
Example sorcery.conf:
[res_pjsip]
endpoint=config,pjsip.conf,criteria=type=endpoint
endpoint=realtime,ps_endpoints,allow_unqualified_fetch=error
ASTERISK-25826 #close
Reported-by: Ross Beer
Tested-by: Ross Beer
Change-Id: Id2691e447db90892890036e663aaf907b2dc1c67
2016-03-08 21:55:30 +00:00
|
|
|
unsigned int ast_sip_get_contact_expiration_check_interval(void)
|
|
|
|
{
|
|
|
|
unsigned int interval;
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
|
|
|
return DEFAULT_CONTACT_EXPIRATION_CHECK_INTERVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
interval = cfg->contact_expiration_check_interval;
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return interval;
|
|
|
|
}
|
|
|
|
|
2016-04-15 16:59:42 +00:00
|
|
|
unsigned int ast_sip_get_disable_multi_domain(void)
|
|
|
|
{
|
|
|
|
unsigned int disable_multi_domain;
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
|
|
|
return DEFAULT_DISABLE_MULTI_DOMAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
disable_multi_domain = cfg->disable_multi_domain;
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return disable_multi_domain;
|
|
|
|
}
|
|
|
|
|
2015-04-11 22:04:32 +00:00
|
|
|
unsigned int ast_sip_get_max_initial_qualify_time(void)
|
|
|
|
{
|
|
|
|
unsigned int time;
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
|
|
|
return DEFAULT_MAX_INITIAL_QUALIFY_TIME;
|
|
|
|
}
|
|
|
|
|
|
|
|
time = cfg->max_initial_qualify_time;
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return time;
|
|
|
|
}
|
|
|
|
|
res_pjsip: Add ability to identify by Authorization username
A feature of chan_sip that service providers relied upon was the ability to
identify by the Authorization username. This is most often used when customers
have a PBX that needs to register rather than identify by IP address. From my
own experiance, this is pretty common with small businesses who otherwise
don't need a static IP.
In this scenario, a register from the customer's PBX may succeed because From
will usually contain the PBXs account id but an INVITE will contain the caller
id. With nothing recognizable in From, the service provider's Asterisk can
never match to an endpoint and the INVITE just stays unauthorized.
The fixes:
A new value "auth_username" has been added to endpoint/identify_by that
will use the username and digest fields in the Authorization header
instead of username and domain in the the From header to match an endpoint,
or the To header to match an aor. This code as added to
res_pjsip_endpoint_identifier_user rather than creating a new module.
Although identify_by was always a comma-separated list, there was only
1 choice so order wasn't preserved. So to keep the order, a vector was added
to the end of ast_sip_endpoint. This is only used by res_pjsip_registrar
to find the aor. The res_pjsip_endpoint_identifier_* modules are called in
globals/endpoint_identifier_order.
Along the way, the logic in res_pjsip_registrar was corrected to match
most-specific to least-specific as res_pjsip_endpoint_identifier_user does.
The order is:
username@domain
username@domain_alias
username
Auth by username does present 1 problem however, the first INVITE won't have
an Authorization header so the distributor, not finding a match on anything,
sends a securty_alert. It still sends a 401 with a challenge so the next
INVITE will have the Authorization header and presumably succeed. As a result
though, that first security alert is actually a false alarm.
To address this, a new feature has been added to pjsip_distributor that keeps
track of unidentified requests and only sends the security alert if a
configurable number of unidentified requests come from the same IP in a
configurable amout of time. Those configuration options have been added to
the global config object. This feature is only used when auth_username
is enabled.
Finally, default_realm was added to the globals object to replace the hard
coded "asterisk" used when an endpoint is not yet identified.
The testsuite tests all pass but new tests are forthcoming for this new
feature.
ASTERISK-25835 #close
Reported-by: Ross Beer
Change-Id: I30ba62d208e6f63439600916fcd1c08a365ed69d
2016-03-08 00:34:31 +00:00
|
|
|
void ast_sip_get_unidentified_request_thresholds(unsigned int *count, unsigned int *period,
|
|
|
|
unsigned int *prune_interval)
|
|
|
|
{
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
|
|
|
*count = DEFAULT_UNIDENTIFIED_REQUEST_COUNT;
|
|
|
|
*period = DEFAULT_UNIDENTIFIED_REQUEST_PERIOD;
|
|
|
|
*prune_interval = DEFAULT_UNIDENTIFIED_REQUEST_PRUNE_INTERVAL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
*count = cfg->unidentified_request_count;
|
|
|
|
*period = cfg->unidentified_request_period;
|
|
|
|
*prune_interval = cfg->unidentified_request_prune_interval;
|
|
|
|
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ast_sip_get_default_realm(char *realm, size_t size)
|
|
|
|
{
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
|
|
|
ast_copy_string(realm, DEFAULT_REALM, size);
|
|
|
|
} else {
|
|
|
|
ast_copy_string(realm, cfg->default_realm, size);
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-10 14:49:45 +00:00
|
|
|
void ast_sip_get_default_from_user(char *from_user, size_t size)
|
2015-09-04 19:40:38 +00:00
|
|
|
{
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
2015-09-10 14:49:45 +00:00
|
|
|
ast_copy_string(from_user, DEFAULT_FROM_USER, size);
|
|
|
|
} else {
|
|
|
|
ast_copy_string(from_user, cfg->default_from_user, size);
|
|
|
|
ao2_ref(cfg, -1);
|
2015-09-04 19:40:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-08 17:53:32 +00:00
|
|
|
|
|
|
|
unsigned int ast_sip_get_mwi_tps_queue_high(void)
|
|
|
|
{
|
|
|
|
unsigned int tps_queue_high;
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
|
|
|
return DEFAULT_MWI_TPS_QUEUE_HIGH;
|
|
|
|
}
|
|
|
|
|
|
|
|
tps_queue_high = cfg->mwi.tps_queue_high;
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return tps_queue_high;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ast_sip_get_mwi_tps_queue_low(void)
|
|
|
|
{
|
|
|
|
int tps_queue_low;
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
|
|
|
return DEFAULT_MWI_TPS_QUEUE_LOW;
|
|
|
|
}
|
|
|
|
|
|
|
|
tps_queue_low = cfg->mwi.tps_queue_low;
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return tps_queue_low;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int ast_sip_get_mwi_disable_initial_unsolicited(void)
|
|
|
|
{
|
|
|
|
unsigned int disable_initial_unsolicited;
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
|
|
|
return DEFAULT_MWI_DISABLE_INITIAL_UNSOLICITED;
|
|
|
|
}
|
|
|
|
|
|
|
|
disable_initial_unsolicited = cfg->mwi.disable_initial_unsolicited;
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return disable_initial_unsolicited;
|
|
|
|
}
|
|
|
|
|
2016-08-29 23:08:22 +00:00
|
|
|
unsigned int ast_sip_get_ignore_uri_user_options(void)
|
|
|
|
{
|
|
|
|
unsigned int ignore_uri_user_options;
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
|
|
|
return DEFAULT_IGNORE_URI_USER_OPTIONS;
|
|
|
|
}
|
|
|
|
|
|
|
|
ignore_uri_user_options = cfg->ignore_uri_user_options;
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return ignore_uri_user_options;
|
|
|
|
}
|
2016-08-08 17:53:32 +00:00
|
|
|
|
2018-10-02 12:31:43 +00:00
|
|
|
unsigned int ast_sip_get_use_callerid_contact(void)
|
|
|
|
{
|
|
|
|
unsigned int use_callerid_contact;
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
|
|
|
return DEFAULT_USE_CALLERID_CONTACT;
|
|
|
|
}
|
|
|
|
|
|
|
|
use_callerid_contact = cfg->use_callerid_contact;
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return use_callerid_contact;
|
|
|
|
}
|
|
|
|
|
2019-01-11 15:48:36 +00:00
|
|
|
unsigned int ast_sip_get_send_contact_status_on_update_registration(void)
|
|
|
|
{
|
|
|
|
unsigned int send_contact_status_on_update_registration;
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
|
|
|
return DEFAULT_SEND_CONTACT_STATUS_ON_UPDATE_REGISTRATION;
|
|
|
|
}
|
|
|
|
|
|
|
|
send_contact_status_on_update_registration = cfg->send_contact_status_on_update_registration;
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return send_contact_status_on_update_registration;
|
|
|
|
}
|
|
|
|
|
2019-02-15 18:53:50 +00:00
|
|
|
enum ast_sip_taskprocessor_overload_trigger ast_sip_get_taskprocessor_overload_trigger(void)
|
|
|
|
{
|
|
|
|
enum ast_sip_taskprocessor_overload_trigger trigger;
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
|
|
|
return DEFAULT_TASKPROCESSOR_OVERLOAD_TRIGGER;
|
|
|
|
}
|
|
|
|
|
|
|
|
trigger = cfg->overload_trigger;
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return trigger;
|
|
|
|
}
|
|
|
|
|
2019-04-08 22:04:48 +00:00
|
|
|
unsigned int ast_sip_get_norefersub(void)
|
|
|
|
{
|
|
|
|
unsigned int norefersub;
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
cfg = get_global_cfg();
|
|
|
|
if (!cfg) {
|
|
|
|
return DEFAULT_NOREFERSUB;
|
|
|
|
}
|
|
|
|
|
|
|
|
norefersub = cfg->norefersub;
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return norefersub;
|
|
|
|
}
|
|
|
|
|
2019-02-15 18:53:50 +00:00
|
|
|
static int overload_trigger_handler(const struct aco_option *opt,
|
|
|
|
struct ast_variable *var, void *obj)
|
|
|
|
{
|
|
|
|
struct global_config *cfg = obj;
|
|
|
|
if (!strcasecmp(var->value, "none")) {
|
|
|
|
cfg->overload_trigger = TASKPROCESSOR_OVERLOAD_TRIGGER_NONE;
|
|
|
|
} else if (!strcasecmp(var->value, "global")) {
|
|
|
|
cfg->overload_trigger = TASKPROCESSOR_OVERLOAD_TRIGGER_GLOBAL;
|
|
|
|
} else if (!strcasecmp(var->value, "pjsip_only")) {
|
|
|
|
cfg->overload_trigger = TASKPROCESSOR_OVERLOAD_TRIGGER_PJSIP_ONLY;
|
|
|
|
} else {
|
|
|
|
ast_log(LOG_WARNING, "Unknown overload trigger '%s' specified for %s\n",
|
|
|
|
var->value, var->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *overload_trigger_map[] = {
|
|
|
|
[TASKPROCESSOR_OVERLOAD_TRIGGER_NONE] = "none",
|
|
|
|
[TASKPROCESSOR_OVERLOAD_TRIGGER_GLOBAL] = "global",
|
|
|
|
[TASKPROCESSOR_OVERLOAD_TRIGGER_PJSIP_ONLY] = "pjsip_only"
|
|
|
|
};
|
|
|
|
|
|
|
|
const char *ast_sip_overload_trigger_to_str(enum ast_sip_taskprocessor_overload_trigger trigger)
|
|
|
|
{
|
|
|
|
return ARRAY_IN_BOUNDS(trigger, overload_trigger_map) ?
|
|
|
|
overload_trigger_map[trigger] : "";
|
|
|
|
}
|
|
|
|
|
|
|
|
static int overload_trigger_to_str(const void *obj, const intptr_t *args, char **buf)
|
|
|
|
{
|
|
|
|
const struct global_config *cfg = obj;
|
|
|
|
*buf = ast_strdup(ast_sip_overload_trigger_to_str(cfg->overload_trigger));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-03-11 15:26:32 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Observer to set default global object if none exist.
|
|
|
|
*
|
|
|
|
* \param name Module name owning the sorcery instance.
|
|
|
|
* \param sorcery Instance being observed.
|
|
|
|
* \param object_type Name of object being observed.
|
|
|
|
* \param reloaded Non-zero if the object is being reloaded.
|
|
|
|
*
|
|
|
|
* \return Nothing
|
|
|
|
*/
|
|
|
|
static void global_loaded_observer(const char *name, const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
|
|
|
|
{
|
|
|
|
struct ao2_container *globals;
|
|
|
|
struct global_config *cfg;
|
|
|
|
|
|
|
|
if (strcmp(object_type, "global")) {
|
|
|
|
/* Not interested */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
globals = ast_sorcery_retrieve_by_fields(sorcery, "global",
|
|
|
|
AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
|
|
|
|
if (globals) {
|
|
|
|
int count;
|
|
|
|
|
|
|
|
count = ao2_container_count(globals);
|
|
|
|
ao2_ref(globals, -1);
|
|
|
|
|
|
|
|
if (1 < count) {
|
|
|
|
ast_log(LOG_ERROR,
|
|
|
|
"At most one pjsip.conf type=global object can be defined. You have %d defined.\n",
|
|
|
|
count);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (count) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_debug(1, "No pjsip.conf type=global object exists so applying defaults.\n");
|
|
|
|
cfg = ast_sorcery_alloc(sorcery, "global", NULL);
|
|
|
|
if (!cfg) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
global_apply(sorcery, cfg);
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct ast_sorcery_instance_observer observer_callbacks_global = {
|
|
|
|
.object_type_loaded = global_loaded_observer,
|
|
|
|
};
|
|
|
|
|
2015-04-09 22:07:50 +00:00
|
|
|
int sip_cli_print_global(struct ast_sip_cli_context *context)
|
|
|
|
{
|
|
|
|
struct global_config *cfg = get_global_cfg();
|
|
|
|
|
|
|
|
if (!cfg) {
|
|
|
|
cfg = ast_sorcery_alloc(ast_sip_get_sorcery(), "global", NULL);
|
|
|
|
if (!cfg) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_str_append(&context->output_buffer, 0, "\nGlobal Settings:\n\n");
|
|
|
|
ast_sip_cli_print_sorcery_objectset(cfg, context, 0);
|
|
|
|
|
|
|
|
ao2_ref(cfg, -1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-03-11 15:26:32 +00:00
|
|
|
int ast_sip_destroy_sorcery_global(void)
|
|
|
|
{
|
|
|
|
struct ast_sorcery *sorcery = ast_sip_get_sorcery();
|
|
|
|
|
|
|
|
ast_sorcery_instance_observer_remove(sorcery, &observer_callbacks_global);
|
|
|
|
|
2019-01-15 23:20:30 +00:00
|
|
|
if (previous_regcontext) {
|
|
|
|
ast_context_destroy_by_name(previous_regcontext, "PJSIP");
|
|
|
|
ast_free(previous_regcontext);
|
|
|
|
}
|
|
|
|
|
2016-08-05 01:11:29 +00:00
|
|
|
ao2_t_global_obj_release(global_cfg, "Module is unloading");
|
|
|
|
|
2015-03-11 15:26:32 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-01-15 23:20:30 +00:00
|
|
|
|
2014-02-06 17:55:45 +00:00
|
|
|
int ast_sip_initialize_sorcery_global(void)
|
2013-07-18 19:25:51 +00:00
|
|
|
{
|
2014-02-06 17:55:45 +00:00
|
|
|
struct ast_sorcery *sorcery = ast_sip_get_sorcery();
|
|
|
|
|
2015-03-11 15:26:32 +00:00
|
|
|
snprintf(default_useragent, sizeof(default_useragent), "%s %s",
|
|
|
|
DEFAULT_USERAGENT_PREFIX, ast_get_version());
|
2013-07-18 19:25:51 +00:00
|
|
|
|
2018-07-18 16:12:59 +00:00
|
|
|
ast_sorcery_apply_default(sorcery, "global", "config", "pjsip.conf,criteria=type=global,single_object=yes,explicit_name=global");
|
2013-07-18 19:25:51 +00:00
|
|
|
|
|
|
|
if (ast_sorcery_object_register(sorcery, "global", global_alloc, NULL, global_apply)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "type", "", OPT_NOOP_T, 0, 0);
|
2015-03-11 15:26:32 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "max_forwards",
|
|
|
|
__stringify(DEFAULT_MAX_FORWARDS),
|
|
|
|
OPT_UINT_T, 0, FLDSET(struct global_config, max_forwards));
|
2013-11-22 17:27:55 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "user_agent", default_useragent,
|
2015-03-11 15:26:32 +00:00
|
|
|
OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, useragent));
|
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "default_outbound_endpoint",
|
|
|
|
DEFAULT_OUTBOUND_ENDPOINT,
|
|
|
|
OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_outbound_endpoint));
|
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "debug", DEFAULT_DEBUG,
|
|
|
|
OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, debug));
|
2015-03-17 18:22:20 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "endpoint_identifier_order",
|
|
|
|
DEFAULT_ENDPOINT_IDENTIFIER_ORDER,
|
|
|
|
OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, endpoint_identifier_order));
|
2015-03-11 15:26:32 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "keep_alive_interval",
|
|
|
|
__stringify(DEFAULT_KEEPALIVE_INTERVAL),
|
|
|
|
OPT_UINT_T, 0, FLDSET(struct global_config, keep_alive_interval));
|
2015-04-11 22:04:32 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "max_initial_qualify_time",
|
|
|
|
__stringify(DEFAULT_MAX_INITIAL_QUALIFY_TIME),
|
|
|
|
OPT_UINT_T, 0, FLDSET(struct global_config, max_initial_qualify_time));
|
2015-09-04 19:40:38 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "default_from_user", DEFAULT_FROM_USER,
|
|
|
|
OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_from_user));
|
res_pjsip_mwi: Add voicemail extension and mwi_subscribe_replaces_unsolicited
res_pjsip_mwi was missing the chan_sip "vmexten" functionality which adds
the Message-Account header to the MWI NOTIFY. Also, specifying mailboxes
on endpoints for unsolicited mwi and on aors for subscriptions required
that the admin know in advance which the client wanted. If you specified
mailboxes on the endpoint, subscriptions were rejected even if you also
specified mailboxes on the aor.
Voicemail extension:
* Added a global default_voicemail_extension which defaults to "".
* Added voicemail_extension to both endpoint and aor.
* Added ast_sip_subscription_get_dialog for support.
* Added ast_sip_subscription_get_sip_uri for support.
When an unsolicited NOTIFY is constructed, the From header is parsed, the
voicemail extension from the endpoint is substituted for the user, and the
result placed in the Message-Account field in the body.
When a subscribed NOTIFY is constructed, the subscription dialog local uri
is parsed, the voicemail_extension from the aor (looked up from the
subscription resource name) is substituted for the user, and the result
placed in the Message-Account field in the body.
If no voicemail extension was defined, the Message-Account field is not added
to the NOTIFY body.
mwi_subscribe_replaces_unsolicited:
* Added mwi_subscribe_replaces_unsolicited to endpoint.
The previous behavior was to reject a subscribe if a previous internal
subscription for unsolicited MWI was found for the mailbox. That remains the
default. However, if there are mailboxes also set on the aor and the client
subscribes and mwi_subscribe_replaces_unsolicited is set, the existing internal
subscription is removed and replaced with the external subscription. This
allows an admin to configure mailboxes on both the endpoint and aor and allows
the client to select which to use.
ASTERISK-25865 #close
Reported-by: Ross Beer
Change-Id: Ic15a9415091760539c7134a5ba3dc4a6a1217cea
2016-03-25 03:55:03 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "default_voicemail_extension",
|
|
|
|
DEFAULT_VOICEMAIL_EXTENSION, OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config,
|
|
|
|
default_voicemail_extension));
|
2016-01-10 22:22:12 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "regcontext", DEFAULT_REGCONTEXT,
|
2016-04-06 21:28:49 +00:00
|
|
|
OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, regcontext));
|
sorcery/res_pjsip: Refactor for realtime performance
There were a number of places in the res_pjsip stack that were getting
all endpoints or all aors, and then filtering them locally.
A good example is pjsip_options which, on startup, retrieves all
endpoints, then the aors for those endpoints, then tests the aors to see
if the qualify_frequency is > 0. One issue was that it never did
anything with the endpoints other than retrieve the aors so we probably
could have skipped a step and just retrieved all aors. But nevermind.
This worked reasonably well with local config files but with a realtime
backend and thousands of objects, this was a nightmare. The issue
really boiled down to the fact that while realtime supports predicates
that are passed to the database engine, the non-realtime sorcery
backends didn't.
They do now.
The realtime engines have a scheme for doing simple comparisons. They
take in an ast_variable (or list) for matching, and the name of each
variable can contain an operator. For instance, a name of
"qualify_frequency >" and a value of "0" would create a SQL predicate
that looks like "where qualify_frequency > '0'". If there's no operator
after the name, the engines add an '=' so a simple name of
"qualify_frequency" and a value of "10" would return exact matches.
The non-realtime backends decide whether to include an object in a
result set by calling ast_sorcery_changeset_create on every object in
the internal container. However, ast_sorcery_changeset_create only does
exact string matches though so a name of "qualify_frequency >" and a
value of "0" returns nothing because the literal "qualify_frequency >"
doesn't match any name in the objset set.
So, the real task was to create a generic string matcher that can take a
left value, operator and a right value and perform the match. To that
end, strings.c has a new ast_strings_match(left, operator, right)
function. Left and right are the strings to operate on and the operator
can be a string containing any of the following: = (or NULL or ""), !=,
>, >=, <, <=, like or regex. If the operator is like or regex, the
right string should be a %-pattern or a regex expression. If both left
and right can be converted to float, then a numeric comparison is
performed, otherwise a string comparison is performed.
To use this new function on ast_variables, 2 new functions were added to
config.c. One that compares 2 ast_variables, and one that compares 2
ast_variable lists. The former is useful when you want to compare 2
ast_variables that happen to be in a list but don't want to traverse the
list. The latter will traverse the right list and return true if all
the variables in it match the left list.
Now, the backends' fields_cmp functions call ast_variable_lists_match
instead of ast_sorcery_changeset_create and they can now process the
same syntax as the realtime engines. The realtime backend just passes
the variable list unaltered to the engine. The only gotcha is that
there's no common realtime engine support for regex so that's been noted
in the api docs for ast_sorcery_retrieve_by_fields.
Only one more change to sorcery was done... A new config flag
"allow_unqualified_fetch" was added to reg_sorcery_realtime.
"no": ignore fetches if no predicate fields were supplied.
"error": same as no but emit an error. (good for testing)
"yes": allow (the default);
"warn": allow but emit a warning. (good for testing)
Now on to res_pjsip...
pjsip_options was modified to retrieve aors with qualify_frequency > 0
rather than all endpoints then all aors. Not only was this a big
improvement in realtime retrieval but even for config files there's an
improvement because we're not going through endpoints anymore.
res_pjsip_mwi was modified to retieve only endpoints with something in
the mailboxes field instead of all endpoints then testing mailboxes.
res_pjsip_registrar_expire was completely refactored. It was retrieving
all contacts then setting up scheduler entries to check for expiration.
Now, it's a single thread (like keepalive) that periodically retrieves
only contacts whose expiration time is < now and deletes them. A new
contact_expiration_check_interval was added to global with a default of
30 seconds.
Ross Beer reports that with this patch, his Asterisk startup time dropped
from around an hour to under 30 seconds.
There are still objects that can't be filtered at the database like
identifies, transports, and registrations. These are not going to be
anywhere near as numerous as endpoints, aors, auths, contacts however.
Back to allow_unqualified_fetch. If this is set to yes and you have a
very large number of objects in the database, the pjsip CLI commands
will attempt to retrive ALL of them if not qualified with a LIKE.
Worse, if you type "pjsip show endpoint <tab>" guess what's going to
happen? :) Having a cache helps but all the objects will have to be
retrieved at least once to fill the cache. Setting
allow_unqualified_fetch=no prevents the mass retrieve and should be used
on endpoints, auths, aors, and contacts. It should NOT be used for
identifies, registrations and transports since these MUST be
retrieved in bulk.
Example sorcery.conf:
[res_pjsip]
endpoint=config,pjsip.conf,criteria=type=endpoint
endpoint=realtime,ps_endpoints,allow_unqualified_fetch=error
ASTERISK-25826 #close
Reported-by: Ross Beer
Tested-by: Ross Beer
Change-Id: Id2691e447db90892890036e663aaf907b2dc1c67
2016-03-08 21:55:30 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "contact_expiration_check_interval",
|
|
|
|
__stringify(DEFAULT_CONTACT_EXPIRATION_CHECK_INTERVAL),
|
2016-04-06 21:28:49 +00:00
|
|
|
OPT_UINT_T, 0, FLDSET(struct global_config, contact_expiration_check_interval));
|
2016-08-30 22:26:43 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "disable_multi_domain",
|
|
|
|
DEFAULT_DISABLE_MULTI_DOMAIN ? "yes" : "no",
|
2016-04-15 16:59:42 +00:00
|
|
|
OPT_BOOL_T, 1, FLDSET(struct global_config, disable_multi_domain));
|
res_pjsip: Add ability to identify by Authorization username
A feature of chan_sip that service providers relied upon was the ability to
identify by the Authorization username. This is most often used when customers
have a PBX that needs to register rather than identify by IP address. From my
own experiance, this is pretty common with small businesses who otherwise
don't need a static IP.
In this scenario, a register from the customer's PBX may succeed because From
will usually contain the PBXs account id but an INVITE will contain the caller
id. With nothing recognizable in From, the service provider's Asterisk can
never match to an endpoint and the INVITE just stays unauthorized.
The fixes:
A new value "auth_username" has been added to endpoint/identify_by that
will use the username and digest fields in the Authorization header
instead of username and domain in the the From header to match an endpoint,
or the To header to match an aor. This code as added to
res_pjsip_endpoint_identifier_user rather than creating a new module.
Although identify_by was always a comma-separated list, there was only
1 choice so order wasn't preserved. So to keep the order, a vector was added
to the end of ast_sip_endpoint. This is only used by res_pjsip_registrar
to find the aor. The res_pjsip_endpoint_identifier_* modules are called in
globals/endpoint_identifier_order.
Along the way, the logic in res_pjsip_registrar was corrected to match
most-specific to least-specific as res_pjsip_endpoint_identifier_user does.
The order is:
username@domain
username@domain_alias
username
Auth by username does present 1 problem however, the first INVITE won't have
an Authorization header so the distributor, not finding a match on anything,
sends a securty_alert. It still sends a 401 with a challenge so the next
INVITE will have the Authorization header and presumably succeed. As a result
though, that first security alert is actually a false alarm.
To address this, a new feature has been added to pjsip_distributor that keeps
track of unidentified requests and only sends the security alert if a
configurable number of unidentified requests come from the same IP in a
configurable amout of time. Those configuration options have been added to
the global config object. This feature is only used when auth_username
is enabled.
Finally, default_realm was added to the globals object to replace the hard
coded "asterisk" used when an endpoint is not yet identified.
The testsuite tests all pass but new tests are forthcoming for this new
feature.
ASTERISK-25835 #close
Reported-by: Ross Beer
Change-Id: I30ba62d208e6f63439600916fcd1c08a365ed69d
2016-03-08 00:34:31 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "unidentified_request_count",
|
|
|
|
__stringify(DEFAULT_UNIDENTIFIED_REQUEST_COUNT),
|
|
|
|
OPT_UINT_T, 0, FLDSET(struct global_config, unidentified_request_count));
|
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "unidentified_request_period",
|
|
|
|
__stringify(DEFAULT_UNIDENTIFIED_REQUEST_PERIOD),
|
|
|
|
OPT_UINT_T, 0, FLDSET(struct global_config, unidentified_request_period));
|
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "unidentified_request_prune_interval",
|
|
|
|
__stringify(DEFAULT_UNIDENTIFIED_REQUEST_PRUNE_INTERVAL),
|
|
|
|
OPT_UINT_T, 0, FLDSET(struct global_config, unidentified_request_prune_interval));
|
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "default_realm", DEFAULT_REALM,
|
|
|
|
OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_realm));
|
2016-08-08 17:53:32 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "mwi_tps_queue_high",
|
|
|
|
__stringify(DEFAULT_MWI_TPS_QUEUE_HIGH),
|
|
|
|
OPT_UINT_T, 0, FLDSET(struct global_config, mwi.tps_queue_high));
|
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "mwi_tps_queue_low",
|
|
|
|
__stringify(DEFAULT_MWI_TPS_QUEUE_LOW),
|
|
|
|
OPT_INT_T, 0, FLDSET(struct global_config, mwi.tps_queue_low));
|
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "mwi_disable_initial_unsolicited",
|
|
|
|
DEFAULT_MWI_DISABLE_INITIAL_UNSOLICITED ? "yes" : "no",
|
|
|
|
OPT_BOOL_T, 1, FLDSET(struct global_config, mwi.disable_initial_unsolicited));
|
2016-08-29 23:08:22 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "ignore_uri_user_options",
|
|
|
|
DEFAULT_IGNORE_URI_USER_OPTIONS ? "yes" : "no",
|
|
|
|
OPT_BOOL_T, 1, FLDSET(struct global_config, ignore_uri_user_options));
|
2018-10-02 12:31:43 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "use_callerid_contact",
|
|
|
|
DEFAULT_USE_CALLERID_CONTACT ? "yes" : "no",
|
|
|
|
OPT_YESNO_T, 1, FLDSET(struct global_config, use_callerid_contact));
|
2019-01-11 15:48:36 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "send_contact_status_on_update_registration",
|
|
|
|
DEFAULT_SEND_CONTACT_STATUS_ON_UPDATE_REGISTRATION ? "yes" : "no",
|
|
|
|
OPT_YESNO_T, 1, FLDSET(struct global_config, send_contact_status_on_update_registration));
|
2019-02-15 18:53:50 +00:00
|
|
|
ast_sorcery_object_field_register_custom(sorcery, "global", "taskprocessor_overload_trigger",
|
|
|
|
overload_trigger_map[DEFAULT_TASKPROCESSOR_OVERLOAD_TRIGGER],
|
|
|
|
overload_trigger_handler, overload_trigger_to_str, NULL, 0, 0);
|
2019-04-08 22:04:48 +00:00
|
|
|
ast_sorcery_object_field_register(sorcery, "global", "norefersub",
|
|
|
|
DEFAULT_NOREFERSUB ? "yes" : "no",
|
|
|
|
OPT_YESNO_T, 1, FLDSET(struct global_config, norefersub));
|
2015-03-11 15:26:32 +00:00
|
|
|
|
|
|
|
if (ast_sorcery_instance_observer_add(sorcery, &observer_callbacks_global)) {
|
|
|
|
return -1;
|
|
|
|
}
|
2013-07-18 19:25:51 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|