mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-29 18:19:30 +00:00
The large GULP->PJSIP renaming effort.
The general gist is to have a clear boundary between old SIP stuff and new SIP stuff by having the word "SIP" for old stuff and "PJSIP" for new stuff. Here's a brief rundown of the changes: * The word "Gulp" in dialstrings, functions, and CLI commands is now "PJSIP" * chan_gulp.c is now chan_pjsip.c * Function names in chan_gulp.c that were "gulp_*" are now "chan_pjsip_*" * All files that were "res_sip*" are now "res_pjsip*" * The "res_sip" directory is now "res_pjsip" * Files in the "res_pjsip" directory that began with "sip_*" are now "pjsip_*" * The configuration file is now "pjsip.conf" instead of "res_sip.conf" * The module info for all PJSIP-related files now uses "PJSIP" instead of "SIP" * CLI and AMI commands created by Asterisk's PJSIP modules now have "pjsip" as the starting word instead of "sip" git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@395764 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
127
res/res_pjsip/config_auth.c
Normal file
127
res/res_pjsip/config_auth.c
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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>
|
||||
#include "asterisk/res_pjsip.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/sorcery.h"
|
||||
|
||||
static void auth_destroy(void *obj)
|
||||
{
|
||||
struct ast_sip_auth *auth = obj;
|
||||
ast_string_field_free_memory(auth);
|
||||
}
|
||||
|
||||
static void *auth_alloc(const char *name)
|
||||
{
|
||||
struct ast_sip_auth *auth = ast_sorcery_generic_alloc(sizeof(*auth), auth_destroy);
|
||||
|
||||
if (!auth) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ast_string_field_init(auth, 64)) {
|
||||
ao2_cleanup(auth);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
static int auth_type_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_auth *auth = obj;
|
||||
if (!strcasecmp(var->value, "userpass")) {
|
||||
auth->type = AST_SIP_AUTH_TYPE_USER_PASS;
|
||||
} else if (!strcasecmp(var->value, "md5")) {
|
||||
auth->type = AST_SIP_AUTH_TYPE_MD5;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Unknown authentication storage type '%s' specified for %s\n",
|
||||
var->value, var->name);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auth_apply(const struct ast_sorcery *sorcery, void *obj)
|
||||
{
|
||||
struct ast_sip_auth *auth = obj;
|
||||
int res = 0;
|
||||
|
||||
if (ast_strlen_zero(auth->auth_user)) {
|
||||
ast_log(LOG_ERROR, "No authentication username for auth '%s'\n",
|
||||
ast_sorcery_object_get_id(auth));
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (auth->type) {
|
||||
case AST_SIP_AUTH_TYPE_USER_PASS:
|
||||
if (ast_strlen_zero(auth->auth_pass)) {
|
||||
ast_log(LOG_ERROR, "'userpass' authentication specified but no"
|
||||
"password specified for auth '%s'\n", ast_sorcery_object_get_id(auth));
|
||||
res = -1;
|
||||
}
|
||||
break;
|
||||
case AST_SIP_AUTH_TYPE_MD5:
|
||||
if (ast_strlen_zero(auth->md5_creds)) {
|
||||
ast_log(LOG_ERROR, "'md5' authentication specified but no md5_cred"
|
||||
"specified for auth '%s'\n", ast_sorcery_object_get_id(auth));
|
||||
res = -1;
|
||||
} else if (strlen(auth->md5_creds) != PJSIP_MD5STRLEN) {
|
||||
ast_log(LOG_ERROR, "'md5' authentication requires digest of size '%d', but"
|
||||
"digest is '%d' in size for auth '%s'\n", PJSIP_MD5STRLEN, (int)strlen(auth->md5_creds),
|
||||
ast_sorcery_object_get_id(auth));
|
||||
res = -1;
|
||||
}
|
||||
break;
|
||||
case AST_SIP_AUTH_TYPE_ARTIFICIAL:
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*! \brief Initialize sorcery with auth support */
|
||||
int ast_sip_initialize_sorcery_auth(struct ast_sorcery *sorcery)
|
||||
{
|
||||
ast_sorcery_apply_default(sorcery, SIP_SORCERY_AUTH_TYPE, "config", "pjsip.conf,criteria=type=auth");
|
||||
|
||||
if (ast_sorcery_object_register(sorcery, SIP_SORCERY_AUTH_TYPE, auth_alloc, NULL, auth_apply)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "type", "",
|
||||
OPT_NOOP_T, 0, 0);
|
||||
ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "username",
|
||||
"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_user));
|
||||
ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "password",
|
||||
"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_pass));
|
||||
ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "md5_cred",
|
||||
"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, md5_creds));
|
||||
ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "realm",
|
||||
"asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, realm));
|
||||
ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "nonce_lifetime",
|
||||
"32", OPT_UINT_T, 0, FLDSET(struct ast_sip_auth, nonce_lifetime));
|
||||
ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_AUTH_TYPE, "auth_type",
|
||||
"userpass", auth_type_handler, NULL, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
65
res/res_pjsip/config_domain_aliases.c
Normal file
65
res/res_pjsip/config_domain_aliases.c
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2013, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@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"
|
||||
#include "asterisk/res_pjsip.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/sorcery.h"
|
||||
|
||||
static void domain_alias_destroy(void *obj)
|
||||
{
|
||||
struct ast_sip_domain_alias *alias = obj;
|
||||
|
||||
ast_string_field_free_memory(alias);
|
||||
}
|
||||
|
||||
static void *domain_alias_alloc(const char *name)
|
||||
{
|
||||
struct ast_sip_domain_alias *alias = ast_sorcery_generic_alloc(sizeof(*alias), domain_alias_destroy);
|
||||
|
||||
if (!alias) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ast_string_field_init(alias, 256)) {
|
||||
ao2_cleanup(alias);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return alias;
|
||||
}
|
||||
|
||||
/*! \brief Initialize sorcery with domain alias support */
|
||||
int ast_sip_initialize_sorcery_domain_alias(struct ast_sorcery *sorcery)
|
||||
{
|
||||
ast_sorcery_apply_default(sorcery, SIP_SORCERY_DOMAIN_ALIAS_TYPE, "config", "pjsip.conf,criteria=type=domain_alias");
|
||||
|
||||
if (ast_sorcery_object_register(sorcery, SIP_SORCERY_DOMAIN_ALIAS_TYPE, domain_alias_alloc, NULL, NULL)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_sorcery_object_field_register(sorcery, SIP_SORCERY_DOMAIN_ALIAS_TYPE, "type", "",
|
||||
OPT_NOOP_T, 0, 0);
|
||||
ast_sorcery_object_field_register(sorcery, SIP_SORCERY_DOMAIN_ALIAS_TYPE, "domain",
|
||||
"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_domain_alias, domain));
|
||||
|
||||
return 0;
|
||||
}
|
90
res/res_pjsip/config_global.c
Normal file
90
res/res_pjsip/config_global.c
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
#include "asterisk/res_pjsip.h"
|
||||
#include "asterisk/sorcery.h"
|
||||
#include "asterisk/ast_version.h"
|
||||
|
||||
#define DEFAULT_MAX_FORWARDS 70
|
||||
#define DEFAULT_USERAGENT_PREFIX "Asterisk PBX"
|
||||
|
||||
static char default_useragent[128];
|
||||
|
||||
struct global_config {
|
||||
SORCERY_OBJECT(details);
|
||||
AST_DECLARE_STRING_FIELDS(
|
||||
AST_STRING_FIELD(useragent);
|
||||
);
|
||||
/* Value to put in Max-Forwards header */
|
||||
unsigned int max_forwards;
|
||||
};
|
||||
|
||||
static void global_destructor(void *obj)
|
||||
{
|
||||
struct global_config *cfg = obj;
|
||||
|
||||
ast_string_field_free_memory(cfg);
|
||||
}
|
||||
|
||||
static void *global_alloc(const char *name)
|
||||
{
|
||||
struct global_config *cfg = ast_sorcery_generic_alloc(sizeof(*cfg), global_destructor);
|
||||
|
||||
if (!cfg || ast_string_field_init(cfg, 64)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
static int global_apply(const struct ast_sorcery *sorcery, void *obj)
|
||||
{
|
||||
struct global_config *cfg = obj;
|
||||
char max_forwards[10];
|
||||
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ast_sip_initialize_sorcery_global(struct ast_sorcery *sorcery)
|
||||
{
|
||||
snprintf(default_useragent, sizeof(default_useragent), "%s %s", DEFAULT_USERAGENT_PREFIX, ast_get_version());
|
||||
|
||||
ast_sorcery_apply_default(sorcery, "global", "config", "pjsip.conf,criteria=type=global");
|
||||
|
||||
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);
|
||||
ast_sorcery_object_field_register(sorcery, "global", "maxforwards", __stringify(DEFAULT_MAX_FORWARDS),
|
||||
OPT_UINT_T, 0, FLDSET(struct global_config, max_forwards));
|
||||
ast_sorcery_object_field_register(sorcery, "global", "useragent", default_useragent,
|
||||
OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, useragent));
|
||||
|
||||
return 0;
|
||||
}
|
88
res/res_pjsip/config_security.c
Normal file
88
res/res_pjsip/config_security.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2013, Digium, Inc.
|
||||
*
|
||||
* Mark Michelson <mmichelson@digium.com>
|
||||
* Kevin Harwell <kharwell@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.
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>pjproject</depend>
|
||||
<depend>res_pjsip</depend>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <pjsip.h>
|
||||
|
||||
#include "asterisk/res_pjsip.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/sorcery.h"
|
||||
#include "asterisk/acl.h"
|
||||
|
||||
static int acl_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_security *security = obj;
|
||||
int error = 0;
|
||||
int ignore;
|
||||
if (!strncmp(var->name, "contact", 7)) {
|
||||
ast_append_acl(var->name + 7, var->value, &security->contact_acl, &error, &ignore);
|
||||
} else {
|
||||
ast_append_acl(var->name, var->value, &security->acl, &error, &ignore);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void security_destroy(void *obj)
|
||||
{
|
||||
struct ast_sip_security *security = obj;
|
||||
security->acl = ast_free_acl_list(security->acl);
|
||||
security->contact_acl = ast_free_acl_list(security->contact_acl);
|
||||
}
|
||||
|
||||
static void *security_alloc(const char *name)
|
||||
{
|
||||
struct ast_sip_security *security =
|
||||
ast_sorcery_generic_alloc(sizeof(*security), security_destroy);
|
||||
|
||||
if (!security) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return security;
|
||||
}
|
||||
|
||||
int ast_sip_initialize_sorcery_security(struct ast_sorcery *sorcery)
|
||||
{
|
||||
ast_sorcery_apply_default(sorcery, SIP_SORCERY_SECURITY_TYPE,
|
||||
"config", "pjsip.conf,criteria=type=security");
|
||||
|
||||
if (ast_sorcery_object_register(sorcery, SIP_SORCERY_SECURITY_TYPE,
|
||||
security_alloc, NULL, NULL)) {
|
||||
|
||||
ast_log(LOG_ERROR, "Failed to register SIP %s object with sorcery\n",
|
||||
SIP_SORCERY_SECURITY_TYPE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_sorcery_object_field_register(sorcery, SIP_SORCERY_SECURITY_TYPE, "type", "", OPT_NOOP_T, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_SECURITY_TYPE, "permit", "", acl_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_SECURITY_TYPE, "deny", "", acl_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_SECURITY_TYPE, "acl", "", acl_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_SECURITY_TYPE, "contactpermit", "", acl_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_SECURITY_TYPE, "contactdeny", "", acl_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_SECURITY_TYPE, "contactacl", "", acl_handler, NULL, 0, 0);
|
||||
return 0;
|
||||
}
|
112
res/res_pjsip/config_system.c
Normal file
112
res/res_pjsip/config_system.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
#include "asterisk/res_pjsip.h"
|
||||
#include "asterisk/sorcery.h"
|
||||
#include "include/res_pjsip_private.h"
|
||||
|
||||
#define TIMER_T1_MIN 100
|
||||
#define DEFAULT_TIMER_T1 500
|
||||
#define DEFAULT_TIMER_B 32000
|
||||
|
||||
struct system_config {
|
||||
SORCERY_OBJECT(details);
|
||||
/*! Transaction Timer T1 value */
|
||||
unsigned int timert1;
|
||||
/*! Transaction Timer B value */
|
||||
unsigned int timerb;
|
||||
/*! Should we use short forms for headers? */
|
||||
unsigned int compactheaders;
|
||||
};
|
||||
|
||||
static struct ast_sorcery *system_sorcery;
|
||||
|
||||
static void *system_alloc(const char *name)
|
||||
{
|
||||
struct system_config *system = ast_sorcery_generic_alloc(sizeof(*system), NULL);
|
||||
|
||||
if (!system) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return system;
|
||||
}
|
||||
|
||||
static int system_apply(const struct ast_sorcery *system_sorcery, void *obj)
|
||||
{
|
||||
struct system_config *system = obj;
|
||||
int min_timerb;
|
||||
|
||||
if (system->timert1 < TIMER_T1_MIN) {
|
||||
ast_log(LOG_WARNING, "Timer T1 setting is too low. Setting to %d\n", TIMER_T1_MIN);
|
||||
system->timert1 = TIMER_T1_MIN;
|
||||
}
|
||||
|
||||
min_timerb = 64 * system->timert1;
|
||||
|
||||
if (system->timerb < min_timerb) {
|
||||
ast_log(LOG_WARNING, "Timer B setting is too low. Setting to %d\n", min_timerb);
|
||||
system->timerb = min_timerb;
|
||||
}
|
||||
|
||||
pjsip_cfg()->tsx.t1 = system->timert1;
|
||||
pjsip_cfg()->tsx.td = system->timerb;
|
||||
|
||||
if (system->compactheaders) {
|
||||
extern pj_bool_t pjsip_use_compact_form;
|
||||
pjsip_use_compact_form = PJ_TRUE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ast_sip_initialize_system(void)
|
||||
{
|
||||
system_sorcery = ast_sorcery_open();
|
||||
if (!system_sorcery) {
|
||||
ast_log(LOG_ERROR, "Failed to open SIP system sorcery\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_sorcery_apply_config(system_sorcery, "res_pjsip");
|
||||
|
||||
ast_sorcery_apply_default(system_sorcery, "system", "config", "pjsip.conf,criteria=type=system");
|
||||
|
||||
if (ast_sorcery_object_register(system_sorcery, "system", system_alloc, NULL, system_apply)) {
|
||||
ast_sorcery_unref(system_sorcery);
|
||||
system_sorcery = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_sorcery_object_field_register(system_sorcery, "system", "type", "", OPT_NOOP_T, 0, 0);
|
||||
ast_sorcery_object_field_register(system_sorcery, "system", "timert1", __stringify(DEFAULT_TIMER_T1),
|
||||
OPT_UINT_T, 0, FLDSET(struct system_config, timert1));
|
||||
ast_sorcery_object_field_register(system_sorcery, "system", "timerb", __stringify(DEFAULT_TIMER_B),
|
||||
OPT_UINT_T, 0, FLDSET(struct system_config, timerb));
|
||||
ast_sorcery_object_field_register(system_sorcery, "system", "compactheaders", "no",
|
||||
OPT_BOOL_T, 1, FLDSET(struct system_config, compactheaders));
|
||||
|
||||
ast_sorcery_load(system_sorcery);
|
||||
|
||||
return 0;
|
||||
}
|
338
res/res_pjsip/config_transport.c
Normal file
338
res/res_pjsip/config_transport.c
Normal file
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2013, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@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>
|
||||
|
||||
#include "asterisk/res_pjsip.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/sorcery.h"
|
||||
#include "asterisk/acl.h"
|
||||
|
||||
static int destroy_transport_state(void *data)
|
||||
{
|
||||
pjsip_transport *transport = data;
|
||||
pjsip_transport_shutdown(transport);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Destructor for transport state information */
|
||||
static void transport_state_destroy(void *obj)
|
||||
{
|
||||
struct ast_sip_transport_state *state = obj;
|
||||
|
||||
if (state->transport) {
|
||||
ast_sip_push_task_synchronous(NULL, destroy_transport_state, state->transport);
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Destructor for transport */
|
||||
static void transport_destroy(void *obj)
|
||||
{
|
||||
struct ast_sip_transport *transport = obj;
|
||||
|
||||
ast_string_field_free_memory(transport);
|
||||
ast_free_ha(transport->localnet);
|
||||
|
||||
if (transport->external_address_refresher) {
|
||||
ast_dnsmgr_release(transport->external_address_refresher);
|
||||
}
|
||||
|
||||
ao2_cleanup(transport->state);
|
||||
}
|
||||
|
||||
/*! \brief Allocator for transport */
|
||||
static void *transport_alloc(const char *name)
|
||||
{
|
||||
struct ast_sip_transport *transport = ast_sorcery_generic_alloc(sizeof(*transport), transport_destroy);
|
||||
|
||||
if (!transport) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ast_string_field_init(transport, 256)) {
|
||||
ao2_cleanup(transport);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pjsip_tls_setting_default(&transport->tls);
|
||||
transport->tls.ciphers = transport->ciphers;
|
||||
|
||||
return transport;
|
||||
}
|
||||
|
||||
static void set_qos(struct ast_sip_transport *transport, pj_qos_params *qos)
|
||||
{
|
||||
if (transport->tos) {
|
||||
qos->flags |= PJ_QOS_PARAM_HAS_DSCP;
|
||||
qos->dscp_val = transport->tos;
|
||||
}
|
||||
if (transport->cos) {
|
||||
qos->flags |= PJ_QOS_PARAM_HAS_SO_PRIO;
|
||||
qos->so_prio = transport->cos;
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Apply handler for transports */
|
||||
static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
|
||||
{
|
||||
struct ast_sip_transport *transport = obj;
|
||||
RAII_VAR(struct ast_sip_transport *, existing, ast_sorcery_retrieve_by_id(sorcery, "transport", ast_sorcery_object_get_id(obj)), ao2_cleanup);
|
||||
pj_status_t res = -1;
|
||||
|
||||
if (!existing || !existing->state) {
|
||||
if (!(transport->state = ao2_alloc(sizeof(*transport->state), transport_state_destroy))) {
|
||||
ast_log(LOG_ERROR, "Transport state for '%s' could not be allocated\n", ast_sorcery_object_get_id(obj));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
transport->state = existing->state;
|
||||
ao2_ref(transport->state, +1);
|
||||
}
|
||||
|
||||
/* Once active a transport can not be reconfigured */
|
||||
if (transport->state->transport || transport->state->factory) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (transport->host.addr.sa_family != PJ_AF_INET && transport->host.addr.sa_family != PJ_AF_INET6) {
|
||||
ast_log(LOG_ERROR, "Transport '%s' could not be started as binding not specified\n", ast_sorcery_object_get_id(obj));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set default port if not present */
|
||||
if (!pj_sockaddr_get_port(&transport->host)) {
|
||||
pj_sockaddr_set_port(&transport->host, (transport->type == AST_TRANSPORT_TLS) ? 5061 : 5060);
|
||||
}
|
||||
|
||||
/* Now that we know what address family we can set up a dnsmgr refresh for the external media address if present */
|
||||
if (!ast_strlen_zero(transport->external_signaling_address)) {
|
||||
if (transport->host.addr.sa_family == pj_AF_INET()) {
|
||||
transport->external_address.ss.ss_family = AF_INET;
|
||||
} else if (transport->host.addr.sa_family == pj_AF_INET6()) {
|
||||
transport->external_address.ss.ss_family = AF_INET6;
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external signaling address\n",
|
||||
ast_sorcery_object_get_id(obj));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_dnsmgr_lookup(transport->external_signaling_address, &transport->external_address, &transport->external_address_refresher, NULL) < 0) {
|
||||
ast_log(LOG_ERROR, "Could not create dnsmgr for external signaling address on '%s'\n", ast_sorcery_object_get_id(obj));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (transport->type == AST_TRANSPORT_UDP) {
|
||||
if (transport->host.addr.sa_family == pj_AF_INET()) {
|
||||
res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(), &transport->host.ipv4, NULL, transport->async_operations, &transport->state->transport);
|
||||
} else if (transport->host.addr.sa_family == pj_AF_INET6()) {
|
||||
res = pjsip_udp_transport_start6(ast_sip_get_pjsip_endpoint(), &transport->host.ipv6, NULL, transport->async_operations, &transport->state->transport);
|
||||
}
|
||||
|
||||
if (res == PJ_SUCCESS && (transport->tos || transport->cos)) {
|
||||
pj_sock_t sock;
|
||||
pj_qos_params qos_params;
|
||||
|
||||
sock = pjsip_udp_transport_get_socket(transport->state->transport);
|
||||
pj_sock_get_qos_params(sock, &qos_params);
|
||||
set_qos(transport, &qos_params);
|
||||
pj_sock_set_qos_params(sock, &qos_params);
|
||||
}
|
||||
} else if (transport->type == AST_TRANSPORT_TCP) {
|
||||
pjsip_tcp_transport_cfg cfg;
|
||||
|
||||
pjsip_tcp_transport_cfg_default(&cfg, transport->host.addr.sa_family);
|
||||
cfg.bind_addr = transport->host;
|
||||
cfg.async_cnt = transport->async_operations;
|
||||
set_qos(transport, &cfg.qos_params);
|
||||
|
||||
res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg, &transport->state->factory);
|
||||
} else if (transport->type == AST_TRANSPORT_TLS) {
|
||||
transport->tls.ca_list_file = pj_str((char*)transport->ca_list_file);
|
||||
transport->tls.cert_file = pj_str((char*)transport->cert_file);
|
||||
transport->tls.privkey_file = pj_str((char*)transport->privkey_file);
|
||||
transport->tls.password = pj_str((char*)transport->password);
|
||||
set_qos(transport, &transport->tls.qos_params);
|
||||
|
||||
res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &transport->tls, &transport->host, NULL, transport->async_operations, &transport->state->factory);
|
||||
} else if ((transport->type == AST_TRANSPORT_WS) || (transport->type == AST_TRANSPORT_WSS)) {
|
||||
if (transport->cos || transport->tos) {
|
||||
ast_log(LOG_WARNING, "TOS and COS values ignored for websocket transport\n");
|
||||
}
|
||||
res = PJ_SUCCESS;
|
||||
}
|
||||
|
||||
if (res != PJ_SUCCESS) {
|
||||
char msg[PJ_ERR_MSG_SIZE];
|
||||
|
||||
pjsip_strerror(res, msg, sizeof(msg));
|
||||
ast_log(LOG_ERROR, "Transport '%s' could not be started: %s\n", ast_sorcery_object_get_id(obj), msg);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Custom handler for turning a string protocol into an enum */
|
||||
static int transport_protocol_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_transport *transport = obj;
|
||||
|
||||
if (!strcasecmp(var->value, "udp")) {
|
||||
transport->type = AST_TRANSPORT_UDP;
|
||||
} else if (!strcasecmp(var->value, "tcp")) {
|
||||
transport->type = AST_TRANSPORT_TCP;
|
||||
} else if (!strcasecmp(var->value, "tls")) {
|
||||
transport->type = AST_TRANSPORT_TLS;
|
||||
} else if (!strcasecmp(var->value, "ws")) {
|
||||
transport->type = AST_TRANSPORT_WS;
|
||||
} else if (!strcasecmp(var->value, "wss")) {
|
||||
transport->type = AST_TRANSPORT_WSS;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Custom handler for turning a string bind into a pj_sockaddr */
|
||||
static int transport_bind_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_transport *transport = obj;
|
||||
pj_str_t buf;
|
||||
|
||||
return (pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, var->value), &transport->host) != PJ_SUCCESS) ? -1 : 0;
|
||||
}
|
||||
|
||||
/*! \brief Custom handler for TLS boolean settings */
|
||||
static int transport_tls_bool_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_transport *transport = obj;
|
||||
|
||||
if (!strcasecmp(var->name, "verify_server")) {
|
||||
transport->tls.verify_server = ast_true(var->value) ? PJ_TRUE : PJ_FALSE;
|
||||
} else if (!strcasecmp(var->name, "verify_client")) {
|
||||
transport->tls.verify_client = ast_true(var->value) ? PJ_TRUE : PJ_FALSE;
|
||||
} else if (!strcasecmp(var->name, "require_client_cert")) {
|
||||
transport->tls.require_client_cert = ast_true(var->value) ? PJ_TRUE : PJ_FALSE;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Custom handler for TLS method setting */
|
||||
static int transport_tls_method_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_transport *transport = obj;
|
||||
|
||||
if (!strcasecmp(var->value, "default")) {
|
||||
transport->tls.method = PJSIP_SSL_DEFAULT_METHOD;
|
||||
} else if (!strcasecmp(var->value, "unspecified")) {
|
||||
transport->tls.method = PJSIP_SSL_UNSPECIFIED_METHOD;
|
||||
} else if (!strcasecmp(var->value, "tlsv1")) {
|
||||
transport->tls.method = PJSIP_TLSV1_METHOD;
|
||||
} else if (!strcasecmp(var->value, "sslv2")) {
|
||||
transport->tls.method = PJSIP_SSLV2_METHOD;
|
||||
} else if (!strcasecmp(var->value, "sslv3")) {
|
||||
transport->tls.method = PJSIP_SSLV3_METHOD;
|
||||
} else if (!strcasecmp(var->value, "sslv23")) {
|
||||
transport->tls.method = PJSIP_SSLV23_METHOD;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Custom handler for TLS cipher setting */
|
||||
static int transport_tls_cipher_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_transport *transport = obj;
|
||||
pj_ssl_cipher cipher;
|
||||
|
||||
if (transport->tls.ciphers_num == (SIP_TLS_MAX_CIPHERS - 1)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TODO: Check this over/tweak - it's taken from pjsua for now */
|
||||
if (!strnicmp(var->value, "0x", 2)) {
|
||||
pj_str_t cipher_st = pj_str((char*)var->value + 2);
|
||||
cipher = pj_strtoul2(&cipher_st, NULL, 16);
|
||||
} else {
|
||||
cipher = atoi(var->value);
|
||||
}
|
||||
|
||||
if (pj_ssl_cipher_is_supported(cipher)) {
|
||||
transport->ciphers[transport->tls.ciphers_num++] = cipher;
|
||||
return 0;
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Cipher '%s' is unsupported\n", var->value);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Custom handler for localnet setting */
|
||||
static int transport_localnet_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_transport *transport = obj;
|
||||
int error = 0;
|
||||
|
||||
if (!(transport->localnet = ast_append_ha("d", var->value, transport->localnet, &error))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/*! \brief Initialize sorcery with transport support */
|
||||
int ast_sip_initialize_sorcery_transport(struct ast_sorcery *sorcery)
|
||||
{
|
||||
ast_sorcery_apply_default(sorcery, "transport", "config", "pjsip.conf,criteria=type=transport");
|
||||
|
||||
if (ast_sorcery_object_register(sorcery, "transport", transport_alloc, NULL, transport_apply)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_sorcery_object_field_register(sorcery, "transport", "type", "", OPT_NOOP_T, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, "transport", "protocol", "udp", transport_protocol_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, "transport", "bind", "", transport_bind_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register(sorcery, "transport", "async_operations", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, async_operations));
|
||||
ast_sorcery_object_field_register(sorcery, "transport", "ca_list_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, ca_list_file));
|
||||
ast_sorcery_object_field_register(sorcery, "transport", "cert_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, cert_file));
|
||||
ast_sorcery_object_field_register(sorcery, "transport", "privkey_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, privkey_file));
|
||||
ast_sorcery_object_field_register(sorcery, "transport", "password", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, password));
|
||||
ast_sorcery_object_field_register(sorcery, "transport", "external_signaling_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, external_signaling_address));
|
||||
ast_sorcery_object_field_register(sorcery, "transport", "external_signaling_port", "0", OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_transport, external_signaling_port), 0, 65535);
|
||||
ast_sorcery_object_field_register(sorcery, "transport", "external_media_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, external_media_address));
|
||||
ast_sorcery_object_field_register(sorcery, "transport", "domain", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, domain));
|
||||
ast_sorcery_object_field_register_custom(sorcery, "transport", "verify_server", "", transport_tls_bool_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, "transport", "verify_client", "", transport_tls_bool_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, "transport", "require_client_cert", "", transport_tls_bool_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, "transport", "method", "", transport_tls_method_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, "transport", "cipher", "", transport_tls_cipher_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, "transport", "localnet", "", transport_localnet_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register(sorcery, "transport", "tos", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, tos));
|
||||
ast_sorcery_object_field_register(sorcery, "transport", "cos", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, cos));
|
||||
|
||||
return 0;
|
||||
}
|
74
res/res_pjsip/include/res_pjsip_private.h
Normal file
74
res/res_pjsip/include/res_pjsip_private.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* res_pjsip.h
|
||||
*
|
||||
* Created on: Jan 25, 2013
|
||||
* Author: mjordan
|
||||
*/
|
||||
|
||||
#ifndef RES_SIP_PRIVATE_H_
|
||||
#define RES_SIP_PRIVATE_H_
|
||||
|
||||
struct ao2_container;
|
||||
|
||||
/*!
|
||||
* \brief Initialize the configuration for res_pjsip
|
||||
*/
|
||||
int ast_res_pjsip_initialize_configuration(void);
|
||||
|
||||
/*!
|
||||
* \brief Annihilate the configuration objects
|
||||
*/
|
||||
void ast_res_pjsip_destroy_configuration(void);
|
||||
|
||||
/*!
|
||||
* \brief Reload the configuration
|
||||
*/
|
||||
int ast_res_pjsip_reload_configuration(void);
|
||||
|
||||
/*!
|
||||
* \brief Initialize OPTIONS request handling.
|
||||
*
|
||||
* XXX This currently includes qualifying peers. It shouldn't.
|
||||
* That should go into a registrar. When that occurs, we won't
|
||||
* need the reload stuff.
|
||||
*
|
||||
* \param reload Reload options handling
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval other on failure
|
||||
*/
|
||||
int ast_res_pjsip_init_options_handling(int reload);
|
||||
|
||||
/*!
|
||||
* \brief Initialize transport storage for contacts.
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval other on failure
|
||||
*/
|
||||
int ast_res_pjsip_init_contact_transports(void);
|
||||
|
||||
/*!
|
||||
* \brief Initialize outbound authentication support
|
||||
*
|
||||
* \retval 0 Success
|
||||
* \retval non-zero Failure
|
||||
*/
|
||||
int ast_sip_initialize_outbound_authentication(void);
|
||||
|
||||
/*!
|
||||
* \brief Initialize system configuration
|
||||
*
|
||||
* \retval 0 Success
|
||||
* \retval non-zero Failure
|
||||
*/
|
||||
int ast_sip_initialize_system(void);
|
||||
|
||||
/*!
|
||||
* \brief Initialize global configuration
|
||||
*
|
||||
* \retval 0 Success
|
||||
* \retval non-zero Failure
|
||||
*/
|
||||
int ast_sip_initialize_global(void);
|
||||
|
||||
#endif /* RES_SIP_PRIVATE_H_ */
|
328
res/res_pjsip/location.c
Normal file
328
res/res_pjsip/location.c
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2013, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@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"
|
||||
|
||||
#include "asterisk/res_pjsip.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/sorcery.h"
|
||||
#include "include/res_pjsip_private.h"
|
||||
|
||||
#define CONTACT_TRANSPORTS_BUCKETS 7
|
||||
static struct ao2_container *contact_transports;
|
||||
|
||||
/*! \brief Destructor for AOR */
|
||||
static void aor_destroy(void *obj)
|
||||
{
|
||||
struct ast_sip_aor *aor = obj;
|
||||
|
||||
ao2_cleanup(aor->permanent_contacts);
|
||||
ast_string_field_free_memory(aor);
|
||||
}
|
||||
|
||||
/*! \brief Allocator for AOR */
|
||||
static void *aor_alloc(const char *name)
|
||||
{
|
||||
struct ast_sip_aor *aor = ast_sorcery_generic_alloc(sizeof(struct ast_sip_aor), aor_destroy);
|
||||
if (!aor) {
|
||||
return NULL;
|
||||
}
|
||||
ast_string_field_init(aor, 128);
|
||||
return aor;
|
||||
}
|
||||
|
||||
/*! \brief Destructor for contact */
|
||||
static void contact_destroy(void *obj)
|
||||
{
|
||||
struct ast_sip_contact *contact = obj;
|
||||
|
||||
ast_string_field_free_memory(contact);
|
||||
}
|
||||
|
||||
/*! \brief Allocator for contact */
|
||||
static void *contact_alloc(const char *name)
|
||||
{
|
||||
struct ast_sip_contact *contact = ast_sorcery_generic_alloc(sizeof(*contact), contact_destroy);
|
||||
|
||||
if (!contact) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ast_string_field_init(contact, 256)) {
|
||||
ao2_cleanup(contact);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return contact;
|
||||
}
|
||||
|
||||
/*! \brief Callback function for finding a contact_transport by URI */
|
||||
static int contact_transport_find_by_uri(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct ast_sip_contact_transport *ct = obj;
|
||||
const char *contact_uri = arg;
|
||||
|
||||
return (!strcmp(ct->uri, contact_uri)) ? CMP_MATCH | CMP_STOP : 0;
|
||||
}
|
||||
|
||||
/*! \brief Callback function for finding a contact_transport by transport */
|
||||
static int contact_transport_find_by_transport(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct ast_sip_contact_transport *ct = obj;
|
||||
pjsip_transport *transport = arg;
|
||||
|
||||
return (ct->transport == transport) ? CMP_MATCH | CMP_STOP : 0;
|
||||
}
|
||||
|
||||
void ast_sip_location_add_contact_transport(struct ast_sip_contact_transport *ct)
|
||||
{
|
||||
ao2_link(contact_transports, ct);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void ast_sip_location_delete_contact_transport(struct ast_sip_contact_transport *ct)
|
||||
{
|
||||
ao2_unlink(contact_transports, ct);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
struct ast_sip_contact_transport *ast_sip_location_retrieve_contact_transport_by_uri(const char *contact_uri)
|
||||
{
|
||||
return ao2_callback(contact_transports, 0, contact_transport_find_by_uri, (void *)contact_uri);
|
||||
}
|
||||
|
||||
struct ast_sip_contact_transport *ast_sip_location_retrieve_contact_transport_by_transport(pjsip_transport *transport)
|
||||
{
|
||||
return ao2_callback(contact_transports, 0, contact_transport_find_by_transport, transport);
|
||||
}
|
||||
|
||||
struct ast_sip_aor *ast_sip_location_retrieve_aor(const char *aor_name)
|
||||
{
|
||||
return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "aor", aor_name);
|
||||
}
|
||||
|
||||
/*! \brief Internal callback function which deletes and unlinks any expired contacts */
|
||||
static int contact_expire(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct ast_sip_contact *contact = obj;
|
||||
|
||||
/* If the contact has not yet expired it is valid */
|
||||
if (ast_tvdiff_ms(contact->expiration_time, ast_tvnow()) > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ast_sip_location_delete_contact(contact);
|
||||
|
||||
return CMP_MATCH;
|
||||
}
|
||||
|
||||
/*! \brief Internal callback function which links static contacts into another container */
|
||||
static int contact_link_static(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct ao2_container *dest = arg;
|
||||
|
||||
ao2_link_flags(dest, obj, OBJ_NOLOCK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Simple callback function which returns immediately, used to grab the first contact of an AOR */
|
||||
static int contact_find_first(void *obj, void *arg, int flags)
|
||||
{
|
||||
return CMP_MATCH | CMP_STOP;
|
||||
}
|
||||
|
||||
struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct ast_sip_aor *aor)
|
||||
{
|
||||
RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
|
||||
struct ast_sip_contact *contact;
|
||||
|
||||
contacts = ast_sip_location_retrieve_aor_contacts(aor);
|
||||
if (!contacts || (ao2_container_count(contacts) == 0)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
contact = ao2_callback(contacts, OBJ_NOLOCK, contact_find_first, NULL);
|
||||
return contact;
|
||||
}
|
||||
|
||||
struct ao2_container *ast_sip_location_retrieve_aor_contacts(const struct ast_sip_aor *aor)
|
||||
{
|
||||
/* Give enough space for ^ at the beginning and ;@ at the end, since that is our object naming scheme */
|
||||
char regex[strlen(ast_sorcery_object_get_id(aor)) + 4];
|
||||
struct ao2_container *contacts;
|
||||
|
||||
snprintf(regex, sizeof(regex), "^%s;@", ast_sorcery_object_get_id(aor));
|
||||
|
||||
if (!(contacts = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "contact", regex))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Prune any expired contacts and delete them, we do this first because static contacts can never expire */
|
||||
ao2_callback(contacts, OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, contact_expire, NULL);
|
||||
|
||||
/* Add any permanent contacts from the AOR */
|
||||
if (aor->permanent_contacts) {
|
||||
ao2_callback(aor->permanent_contacts, OBJ_NOLOCK | OBJ_NODATA, contact_link_static, contacts);
|
||||
}
|
||||
|
||||
return contacts;
|
||||
}
|
||||
|
||||
struct ast_sip_contact *ast_sip_location_retrieve_contact_from_aor_list(const char *aor_list)
|
||||
{
|
||||
char *aor_name;
|
||||
char *rest;
|
||||
struct ast_sip_contact *contact = NULL;
|
||||
|
||||
/* If the location is still empty we have nowhere to go */
|
||||
if (ast_strlen_zero(aor_list) || !(rest = ast_strdupa(aor_list))) {
|
||||
ast_log(LOG_WARNING, "Unable to determine contacts from empty aor list\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while ((aor_name = strsep(&rest, ","))) {
|
||||
RAII_VAR(struct ast_sip_aor *, aor, ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
|
||||
RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
|
||||
|
||||
if (!aor) {
|
||||
continue;
|
||||
}
|
||||
contact = ast_sip_location_retrieve_first_aor_contact(aor);
|
||||
/* If a valid contact is available use its URI for dialing */
|
||||
if (contact) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return contact;
|
||||
}
|
||||
|
||||
struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_name)
|
||||
{
|
||||
return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "contact", contact_name);
|
||||
}
|
||||
|
||||
int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri, struct timeval expiration_time)
|
||||
{
|
||||
char name[AST_UUID_STR_LEN];
|
||||
RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
|
||||
|
||||
snprintf(name, sizeof(name), "%s;@%s", ast_sorcery_object_get_id(aor), uri);
|
||||
|
||||
if (!(contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", name))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_string_field_set(contact, uri, uri);
|
||||
contact->expiration_time = expiration_time;
|
||||
contact->qualify_frequency = aor->qualify_frequency;
|
||||
contact->authenticate_qualify = aor->authenticate_qualify;
|
||||
|
||||
return ast_sorcery_create(ast_sip_get_sorcery(), contact);
|
||||
}
|
||||
|
||||
int ast_sip_location_update_contact(struct ast_sip_contact *contact)
|
||||
{
|
||||
return ast_sorcery_update(ast_sip_get_sorcery(), contact);
|
||||
}
|
||||
|
||||
int ast_sip_location_delete_contact(struct ast_sip_contact *contact)
|
||||
{
|
||||
return ast_sorcery_delete(ast_sip_get_sorcery(), contact);
|
||||
}
|
||||
|
||||
/*! \brief Custom handler for translating from a string timeval to actual structure */
|
||||
static int expiration_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_contact *contact = obj;
|
||||
return ast_get_timeval(var->value, &contact->expiration_time, ast_tv(0, 0), NULL);
|
||||
}
|
||||
|
||||
/*! \brief Custom handler for translating from an actual structure timeval to string */
|
||||
static int expiration_struct2str(const void *obj, const intptr_t *args, char **buf)
|
||||
{
|
||||
const struct ast_sip_contact *contact = obj;
|
||||
return (ast_asprintf(buf, "%lu", contact->expiration_time.tv_sec) < 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
/*! \brief Custom handler for permanent URIs */
|
||||
static int permanent_uri_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_aor *aor = obj;
|
||||
RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
|
||||
|
||||
if ((!aor->permanent_contacts && !(aor->permanent_contacts = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) ||
|
||||
!(contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", NULL))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_string_field_set(contact, uri, var->value);
|
||||
ao2_link_flags(aor->permanent_contacts, contact, OBJ_NOLOCK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Initialize sorcery with location support */
|
||||
int ast_sip_initialize_sorcery_location(struct ast_sorcery *sorcery)
|
||||
{
|
||||
ast_sorcery_apply_default(sorcery, "contact", "astdb", "registrar");
|
||||
ast_sorcery_apply_default(sorcery, "aor", "config", "pjsip.conf,criteria=type=aor");
|
||||
|
||||
if (ast_sorcery_object_register(sorcery, "contact", contact_alloc, NULL, NULL) ||
|
||||
ast_sorcery_object_register(sorcery, "aor", aor_alloc, NULL, NULL)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_sorcery_object_field_register(sorcery, "contact", "type", "", OPT_NOOP_T, 0, 0);
|
||||
ast_sorcery_object_field_register(sorcery, "contact", "uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, uri));
|
||||
ast_sorcery_object_field_register_custom(sorcery, "contact", "expiration_time", "", expiration_str2struct, expiration_struct2str, 0, 0);
|
||||
ast_sorcery_object_field_register(sorcery, "contact", "qualify_frequency", 0, OPT_UINT_T,
|
||||
PARSE_IN_RANGE, FLDSET(struct ast_sip_contact, qualify_frequency), 0, 86400);
|
||||
|
||||
ast_sorcery_object_field_register(sorcery, "aor", "type", "", OPT_NOOP_T, 0, 0);
|
||||
ast_sorcery_object_field_register(sorcery, "aor", "minimum_expiration", "60", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, minimum_expiration));
|
||||
ast_sorcery_object_field_register(sorcery, "aor", "maximum_expiration", "7200", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, maximum_expiration));
|
||||
ast_sorcery_object_field_register(sorcery, "aor", "default_expiration", "3600", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, default_expiration));
|
||||
ast_sorcery_object_field_register(sorcery, "aor", "qualify_frequency", 0, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_aor, qualify_frequency), 0, 86400);
|
||||
ast_sorcery_object_field_register(sorcery, "aor", "authenticate_qualify", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, authenticate_qualify));
|
||||
ast_sorcery_object_field_register(sorcery, "aor", "max_contacts", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, max_contacts));
|
||||
ast_sorcery_object_field_register(sorcery, "aor", "remove_existing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, remove_existing));
|
||||
ast_sorcery_object_field_register_custom(sorcery, "aor", "contact", "", permanent_uri_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register(sorcery, "aor", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, mailboxes));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ast_res_pjsip_init_contact_transports(void)
|
||||
{
|
||||
if (contact_transports) {
|
||||
ao2_t_ref(contact_transports, -1, "Remove old contact transports");
|
||||
}
|
||||
|
||||
contact_transports = ao2_t_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, CONTACT_TRANSPORTS_BUCKETS, NULL, NULL, "Create container for contact transports");
|
||||
if (!contact_transports) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
892
res/res_pjsip/pjsip_configuration.c
Normal file
892
res/res_pjsip/pjsip_configuration.c
Normal file
@@ -0,0 +1,892 @@
|
||||
/*
|
||||
* sip_cli_commands.c
|
||||
*
|
||||
* Created on: Jan 25, 2013
|
||||
* Author: mjordan
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <pjsip.h>
|
||||
#include <pjsip_ua.h>
|
||||
|
||||
#include "asterisk/res_pjsip.h"
|
||||
#include "include/res_pjsip_private.h"
|
||||
#include "asterisk/cli.h"
|
||||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/sorcery.h"
|
||||
#include "asterisk/callerid.h"
|
||||
#include "asterisk/stasis_endpoints.h"
|
||||
|
||||
/*! \brief Number of buckets for persistent endpoint information */
|
||||
#define PERSISTENT_BUCKETS 53
|
||||
|
||||
/*! \brief Persistent endpoint information */
|
||||
struct sip_persistent_endpoint {
|
||||
/*! \brief Asterisk endpoint itself */
|
||||
struct ast_endpoint *endpoint;
|
||||
/*! \brief AORs that we should react to */
|
||||
char *aors;
|
||||
};
|
||||
|
||||
/*! \brief Container for persistent endpoint information */
|
||||
static struct ao2_container *persistent_endpoints;
|
||||
|
||||
static struct ast_sorcery *sip_sorcery;
|
||||
|
||||
/*! \brief Hashing function for persistent endpoint information */
|
||||
static int persistent_endpoint_hash(const void *obj, const int flags)
|
||||
{
|
||||
const struct sip_persistent_endpoint *persistent = obj;
|
||||
const char *id = (flags & OBJ_KEY ? obj : ast_endpoint_get_resource(persistent->endpoint));
|
||||
|
||||
return ast_str_hash(id);
|
||||
}
|
||||
|
||||
/*! \brief Comparison function for persistent endpoint information */
|
||||
static int persistent_endpoint_cmp(void *obj, void *arg, int flags)
|
||||
{
|
||||
const struct sip_persistent_endpoint *persistent1 = obj;
|
||||
const struct sip_persistent_endpoint *persistent2 = arg;
|
||||
const char *id = (flags & OBJ_KEY ? arg : ast_endpoint_get_resource(persistent2->endpoint));
|
||||
|
||||
return !strcmp(ast_endpoint_get_resource(persistent1->endpoint), id) ? CMP_MATCH | CMP_STOP : 0;
|
||||
}
|
||||
|
||||
/*! \brief Callback function for changing the state of an endpoint */
|
||||
static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct sip_persistent_endpoint *persistent = obj;
|
||||
char *aor = arg;
|
||||
RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
|
||||
|
||||
if (!ast_strlen_zero(aor) && !strstr(persistent->aors, aor)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((contact = ast_sip_location_retrieve_contact_from_aor_list(persistent->aors))) {
|
||||
ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_ONLINE);
|
||||
blob = ast_json_pack("{s: s}", "peer_status", "Reachable");
|
||||
} else {
|
||||
ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_OFFLINE);
|
||||
blob = ast_json_pack("{s: s}", "peer_status", "Unreachable");
|
||||
}
|
||||
|
||||
ast_endpoint_blob_publish(persistent->endpoint, ast_endpoint_state_type(), blob);
|
||||
|
||||
ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "PJSIP/%s", ast_endpoint_get_resource(persistent->endpoint));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Function called when stuff relating to a contact happens (created/deleted) */
|
||||
static void persistent_endpoint_contact_observer(const void *object)
|
||||
{
|
||||
char *id = ast_strdupa(ast_sorcery_object_get_id(object)), *aor = NULL;
|
||||
|
||||
aor = strsep(&id, ";@");
|
||||
|
||||
ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor);
|
||||
}
|
||||
|
||||
/*! \brief Observer for contacts so state can be updated on respective endpoints */
|
||||
static struct ast_sorcery_observer state_contact_observer = {
|
||||
.created = persistent_endpoint_contact_observer,
|
||||
.deleted = persistent_endpoint_contact_observer,
|
||||
};
|
||||
|
||||
static char *handle_cli_show_endpoints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
RAII_VAR(struct ao2_container *, endpoints, NULL, ao2_cleanup);
|
||||
struct ao2_iterator it_endpoints;
|
||||
struct ast_sip_endpoint *endpoint;
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "pjsip show endpoints";
|
||||
e->usage =
|
||||
"Usage: pjsip show endpoints\n"
|
||||
" Show the registered PJSIP endpoints\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
endpoints = ast_sip_get_endpoints();
|
||||
if (!endpoints) {
|
||||
return CLI_FAILURE;
|
||||
}
|
||||
|
||||
if (!ao2_container_count(endpoints)) {
|
||||
ast_cli(a->fd, "No endpoints found\n");
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
ast_cli(a->fd, "Endpoints:\n");
|
||||
it_endpoints = ao2_iterator_init(endpoints, 0);
|
||||
while ((endpoint = ao2_iterator_next(&it_endpoints))) {
|
||||
ast_cli(a->fd, "%s\n", ast_sorcery_object_get_id(endpoint));
|
||||
ao2_ref(endpoint, -1);
|
||||
}
|
||||
ao2_iterator_destroy(&it_endpoints);
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static int show_contact(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct ast_sip_contact *contact = obj;
|
||||
struct ast_cli_args *a = arg;
|
||||
RAII_VAR(struct ast_sip_contact_status *, status, ast_sorcery_retrieve_by_id(
|
||||
ast_sip_get_sorcery(), CONTACT_STATUS,
|
||||
ast_sorcery_object_get_id(contact)), ao2_cleanup);
|
||||
|
||||
ast_cli(a->fd, "\tContact %s:\n", contact->uri);
|
||||
|
||||
if (!status) {
|
||||
ast_cli(a->fd, "\tStatus not found!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ast_cli(a->fd, "\t\tavailable = %s\n", status->status ? "yes" : "no");
|
||||
|
||||
if (status->status) {
|
||||
ast_cli(a->fd, "\t\tRTT = %lld microseconds\n", (long long)status->rtt);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void show_endpoint(struct ast_sip_endpoint *endpoint, struct ast_cli_args *a)
|
||||
{
|
||||
char *aor_name, *aors;
|
||||
|
||||
if (ast_strlen_zero(endpoint->aors)) {
|
||||
return;
|
||||
}
|
||||
|
||||
aors = ast_strdupa(endpoint->aors);
|
||||
|
||||
while ((aor_name = strsep(&aors, ","))) {
|
||||
RAII_VAR(struct ast_sip_aor *, aor,
|
||||
ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
|
||||
RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
|
||||
|
||||
if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ast_cli(a->fd, "AOR %s:\n", ast_sorcery_object_get_id(aor));
|
||||
ao2_callback(contacts, OBJ_NODATA, show_contact, a);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static char *cli_show_endpoint(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
|
||||
const char *endpoint_name;
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "pjsip show endpoint";
|
||||
e->usage =
|
||||
"Usage: pjsip show endpoint <endpoint>\n"
|
||||
" Show the given PJSIP endpoint.\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (a->argc != 4) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
endpoint_name = a->argv[3];
|
||||
|
||||
if (!(endpoint = ast_sorcery_retrieve_by_id(
|
||||
ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
|
||||
ast_cli(a->fd, "Unable to retrieve endpoint %s\n", endpoint_name);
|
||||
return CLI_FAILURE;
|
||||
}
|
||||
|
||||
ast_cli(a->fd, "Endpoint %s:\n", endpoint_name);
|
||||
show_endpoint(endpoint, a);
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static struct ast_cli_entry cli_commands[] = {
|
||||
AST_CLI_DEFINE(handle_cli_show_endpoints, "Show PJSIP Endpoints"),
|
||||
AST_CLI_DEFINE(cli_show_endpoint, "Show PJSIP Endpoint")
|
||||
};
|
||||
|
||||
static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
|
||||
if (!strcasecmp(var->value, "rfc4733")) {
|
||||
endpoint->dtmf = AST_SIP_DTMF_RFC_4733;
|
||||
} else if (!strcasecmp(var->value, "inband")) {
|
||||
endpoint->dtmf = AST_SIP_DTMF_INBAND;
|
||||
} else if (!strcasecmp(var->value, "info")) {
|
||||
endpoint->dtmf = AST_SIP_DTMF_INFO;
|
||||
} else if (!strcasecmp(var->value, "none")) {
|
||||
endpoint->dtmf = AST_SIP_DTMF_NONE;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prack_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
|
||||
if (ast_true(var->value)) {
|
||||
endpoint->extensions.flags |= PJSIP_INV_SUPPORT_100REL;
|
||||
} else if (ast_false(var->value)) {
|
||||
endpoint->extensions.flags &= PJSIP_INV_SUPPORT_100REL;
|
||||
} else if (!strcasecmp(var->value, "required")) {
|
||||
endpoint->extensions.flags |= PJSIP_INV_REQUIRE_100REL;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int timers_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
|
||||
if (ast_true(var->value)) {
|
||||
endpoint->extensions.flags |= PJSIP_INV_SUPPORT_TIMER;
|
||||
} else if (ast_false(var->value)) {
|
||||
endpoint->extensions.flags &= PJSIP_INV_SUPPORT_TIMER;
|
||||
} else if (!strcasecmp(var->value, "required")) {
|
||||
endpoint->extensions.flags |= PJSIP_INV_REQUIRE_TIMER;
|
||||
} else if (!strcasecmp(var->value, "always")) {
|
||||
endpoint->extensions.flags |= PJSIP_INV_ALWAYS_USE_TIMER;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ast_sip_auth_array_destroy(struct ast_sip_auth_array *auths)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!auths) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < auths->num; ++i) {
|
||||
ast_free((char *) auths->names[i]);
|
||||
}
|
||||
ast_free(auths->names);
|
||||
auths->num = 0;
|
||||
}
|
||||
|
||||
#define AUTH_INCREMENT 4
|
||||
|
||||
int ast_sip_auth_array_init(struct ast_sip_auth_array *auths, const char *value)
|
||||
{
|
||||
char *auth_names = ast_strdupa(value);
|
||||
char *val;
|
||||
int num_alloced = 0;
|
||||
const char **alloced_auths = NULL;
|
||||
|
||||
while ((val = strsep(&auth_names, ","))) {
|
||||
if (auths->num >= num_alloced) {
|
||||
size_t size;
|
||||
num_alloced += AUTH_INCREMENT;
|
||||
size = num_alloced * sizeof(char *);
|
||||
auths->names = ast_realloc(alloced_auths, size);
|
||||
if (!auths->names) {
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
auths->names[auths->num] = ast_strdup(val);
|
||||
if (!auths->names[auths->num]) {
|
||||
goto failure;
|
||||
}
|
||||
++auths->num;
|
||||
}
|
||||
return 0;
|
||||
|
||||
failure:
|
||||
ast_sip_auth_array_destroy(auths);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int inbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
|
||||
return ast_sip_auth_array_init(&endpoint->inbound_auths, var->value);
|
||||
}
|
||||
|
||||
static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
|
||||
return ast_sip_auth_array_init(&endpoint->outbound_auths, var->value);
|
||||
}
|
||||
|
||||
static int ident_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
char *idents = ast_strdupa(var->value);
|
||||
char *val;
|
||||
|
||||
while ((val = strsep(&idents, ","))) {
|
||||
if (!strcasecmp(val, "username")) {
|
||||
endpoint->ident_method |= AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME;
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Unrecognized identification method %s specified for endpoint %s\n",
|
||||
val, ast_sorcery_object_get_id(endpoint));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int direct_media_method_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
|
||||
if (!strcasecmp(var->value, "invite") || !strcasecmp(var->value, "reinvite")) {
|
||||
endpoint->media.direct_media.method = AST_SIP_SESSION_REFRESH_METHOD_INVITE;
|
||||
} else if (!strcasecmp(var->value, "update")) {
|
||||
endpoint->media.direct_media.method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE;
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "Unrecognized option value %s for %s on endpoint %s\n",
|
||||
var->value, var->name, ast_sorcery_object_get_id(endpoint));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int connected_line_method_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
|
||||
if (!strcasecmp(var->value, "invite") || !strcasecmp(var->value, "reinvite")) {
|
||||
endpoint->id.refresh_method = AST_SIP_SESSION_REFRESH_METHOD_INVITE;
|
||||
} else if (!strcasecmp(var->value, "update")) {
|
||||
endpoint->id.refresh_method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE;
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "Unrecognized option value %s for %s on endpoint %s\n",
|
||||
var->value, var->name, ast_sorcery_object_get_id(endpoint));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int direct_media_glare_mitigation_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
|
||||
if (!strcasecmp(var->value, "none")) {
|
||||
endpoint->media.direct_media.glare_mitigation = AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_NONE;
|
||||
} else if (!strcasecmp(var->value, "outgoing")) {
|
||||
endpoint->media.direct_media.glare_mitigation = AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_OUTGOING;
|
||||
} else if (!strcasecmp(var->value, "incoming")) {
|
||||
endpoint->media.direct_media.glare_mitigation = AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_INCOMING;
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "Unrecognized option value %s for %s on endpoint %s\n",
|
||||
var->value, var->name, ast_sorcery_object_get_id(endpoint));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int caller_id_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
char cid_name[80] = { '\0' };
|
||||
char cid_num[80] = { '\0' };
|
||||
|
||||
ast_callerid_split(var->value, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
|
||||
if (!ast_strlen_zero(cid_name)) {
|
||||
endpoint->id.self.name.str = ast_strdup(cid_name);
|
||||
if (!endpoint->id.self.name.str) {
|
||||
return -1;
|
||||
}
|
||||
endpoint->id.self.name.valid = 1;
|
||||
}
|
||||
if (!ast_strlen_zero(cid_num)) {
|
||||
endpoint->id.self.number.str = ast_strdup(cid_num);
|
||||
if (!endpoint->id.self.number.str) {
|
||||
return -1;
|
||||
}
|
||||
endpoint->id.self.number.valid = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int caller_id_privacy_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
int callingpres = ast_parse_caller_presentation(var->value);
|
||||
if (callingpres == -1 && sscanf(var->value, "%d", &callingpres) != 1) {
|
||||
return -1;
|
||||
}
|
||||
endpoint->id.self.number.presentation = callingpres;
|
||||
endpoint->id.self.name.presentation = callingpres;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int caller_id_tag_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
endpoint->id.self.tag = ast_strdup(var->value);
|
||||
return endpoint->id.self.tag ? 0 : -1;
|
||||
}
|
||||
|
||||
static int media_encryption_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
|
||||
if (!strcasecmp("no", var->value)) {
|
||||
endpoint->media.rtp.encryption = AST_SIP_MEDIA_ENCRYPT_NONE;
|
||||
} else if (!strcasecmp("sdes", var->value)) {
|
||||
endpoint->media.rtp.encryption = AST_SIP_MEDIA_ENCRYPT_SDES;
|
||||
} else if (!strcasecmp("dtls", var->value)) {
|
||||
endpoint->media.rtp.encryption = AST_SIP_MEDIA_ENCRYPT_DTLS;
|
||||
ast_rtp_dtls_cfg_parse(&endpoint->media.rtp.dtls_cfg, "dtlsenable", "yes");
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int group_handler(const struct aco_option *opt,
|
||||
struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
|
||||
if (!strncmp(var->name, "callgroup", 9)) {
|
||||
if (!(endpoint->pickup.callgroup = ast_get_group(var->value))) {
|
||||
return -1;
|
||||
}
|
||||
} else if (!strncmp(var->name, "pickupgroup", 11)) {
|
||||
if (!(endpoint->pickup.pickupgroup = ast_get_group(var->value))) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int named_groups_handler(const struct aco_option *opt,
|
||||
struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
|
||||
if (!strncmp(var->name, "namedcallgroup", 14)) {
|
||||
if (!(endpoint->pickup.named_callgroups =
|
||||
ast_get_namedgroups(var->value))) {
|
||||
return -1;
|
||||
}
|
||||
} else if (!strncmp(var->name, "namedpickupgroup", 16)) {
|
||||
if (!(endpoint->pickup.named_pickupgroups =
|
||||
ast_get_namedgroups(var->value))) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dtls_handler(const struct aco_option *opt,
|
||||
struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
|
||||
return ast_rtp_dtls_cfg_parse(&endpoint->media.rtp.dtls_cfg, var->name, var->value);
|
||||
}
|
||||
|
||||
static int t38udptl_ec_handler(const struct aco_option *opt,
|
||||
struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
|
||||
if (!strcmp(var->value, "none")) {
|
||||
endpoint->media.t38.error_correction = UDPTL_ERROR_CORRECTION_NONE;
|
||||
} else if (!strcmp(var->value, "fec")) {
|
||||
endpoint->media.t38.error_correction = UDPTL_ERROR_CORRECTION_FEC;
|
||||
} else if (!strcmp(var->value, "redundancy")) {
|
||||
endpoint->media.t38.error_correction = UDPTL_ERROR_CORRECTION_REDUNDANCY;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *sip_nat_hook_alloc(const char *name)
|
||||
{
|
||||
return ast_sorcery_generic_alloc(sizeof(struct ast_sip_nat_hook), NULL);
|
||||
}
|
||||
|
||||
/*! \brief Destructor function for persistent endpoint information */
|
||||
static void persistent_endpoint_destroy(void *obj)
|
||||
{
|
||||
struct sip_persistent_endpoint *persistent = obj;
|
||||
|
||||
ast_endpoint_shutdown(persistent->endpoint);
|
||||
ast_free(persistent->aors);
|
||||
}
|
||||
|
||||
/*! \brief Internal function which finds (or creates) persistent endpoint information */
|
||||
static struct ast_endpoint *persistent_endpoint_find_or_create(const struct ast_sip_endpoint *endpoint)
|
||||
{
|
||||
RAII_VAR(struct sip_persistent_endpoint *, persistent, NULL, ao2_cleanup);
|
||||
SCOPED_AO2LOCK(lock, persistent_endpoints);
|
||||
|
||||
if (!(persistent = ao2_find(persistent_endpoints, ast_sorcery_object_get_id(endpoint), OBJ_KEY | OBJ_NOLOCK))) {
|
||||
if (!(persistent = ao2_alloc(sizeof(*persistent), persistent_endpoint_destroy))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(persistent->endpoint = ast_endpoint_create("PJSIP", ast_sorcery_object_get_id(endpoint)))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
persistent->aors = ast_strdup(endpoint->aors);
|
||||
|
||||
if (ast_strlen_zero(persistent->aors)) {
|
||||
ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_UNKNOWN);
|
||||
} else {
|
||||
persistent_endpoint_update_state(persistent, NULL, 0);
|
||||
}
|
||||
|
||||
ao2_link_flags(persistent_endpoints, persistent, OBJ_NOLOCK);
|
||||
}
|
||||
|
||||
ao2_ref(persistent->endpoint, +1);
|
||||
return persistent->endpoint;
|
||||
}
|
||||
|
||||
/*! \brief Callback function for when an object is finalized */
|
||||
static int sip_endpoint_apply_handler(const struct ast_sorcery *sorcery, void *obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
|
||||
if (!(endpoint->persistent = persistent_endpoint_find_or_create(endpoint))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ast_res_pjsip_initialize_configuration(void)
|
||||
{
|
||||
if (ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(persistent_endpoints = ao2_container_alloc(PERSISTENT_BUCKETS, persistent_endpoint_hash, persistent_endpoint_cmp))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(sip_sorcery = ast_sorcery_open())) {
|
||||
ast_log(LOG_ERROR, "Failed to open SIP sorcery failed to open\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_sorcery_apply_config(sip_sorcery, "res_pjsip");
|
||||
|
||||
if (ast_sip_initialize_sorcery_auth(sip_sorcery)) {
|
||||
ast_log(LOG_ERROR, "Failed to register SIP authentication support\n");
|
||||
ast_sorcery_unref(sip_sorcery);
|
||||
sip_sorcery = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_sorcery_apply_default(sip_sorcery, "endpoint", "config", "pjsip.conf,criteria=type=endpoint");
|
||||
|
||||
ast_sorcery_apply_default(sip_sorcery, "nat_hook", "memory", NULL);
|
||||
|
||||
if (ast_sorcery_object_register(sip_sorcery, "endpoint", ast_sip_endpoint_alloc, NULL, sip_endpoint_apply_handler)) {
|
||||
ast_log(LOG_ERROR, "Failed to register SIP endpoint object with sorcery\n");
|
||||
ast_sorcery_unref(sip_sorcery);
|
||||
sip_sorcery = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_sorcery_object_register(sip_sorcery, "nat_hook", sip_nat_hook_alloc, NULL, NULL);
|
||||
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "type", "", OPT_NOOP_T, 0, 0);
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "context", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, context));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "disallow", "", OPT_CODEC_T, 0, FLDSET(struct ast_sip_endpoint, media.prefs, media.codecs));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow", "", OPT_CODEC_T, 1, FLDSET(struct ast_sip_endpoint, media.prefs, media.codecs));
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtmfmode", "rfc4733", dtmf_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_ipv6", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.ipv6));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_symmetric", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.symmetric));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "ice_support", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.ice_support));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "use_ptime", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.use_ptime));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "force_rport", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, nat.force_rport));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rewrite_contact", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, nat.rewrite_contact));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, transport));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, outbound_proxy));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mohsuggest", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, mohsuggest));
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "100rel", "yes", prack_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "timers", "yes", timers_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "timers_min_se", "90", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, extensions.timer.min_se));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "timers_sess_expires", "1800", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, extensions.timer.sess_expires));
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "auth", "", inbound_auth_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "outbound_auth", "", outbound_auth_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "aors", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, aors));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "external_media_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.external_address));
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "identify_by", "username", ident_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "direct_media", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.direct_media.enabled));
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "direct_media_method", "invite", direct_media_method_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "connected_line_method", "invite", connected_line_method_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "direct_media_glare_mitigation", "none", direct_media_glare_mitigation_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "disable_direct_media_on_nat", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.direct_media.disable_on_nat));
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid", "", caller_id_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid_privacy", "", caller_id_privacy_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid_tag", "", caller_id_tag_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "trust_id_inbound", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.trust_inbound));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "trust_id_outbound", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.trust_outbound));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_pai", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_pai));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_rpid", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_rpid));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_diversion", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_diversion));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, subscription.mwi.mailboxes));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "aggregate_mwi", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, subscription.mwi.aggregate));
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "media_encryption", "no", media_encryption_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "use_avpf", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.use_avpf));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "one_touch_recording", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, info.recording.enabled));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "inband_progress", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, inband_progress));
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callgroup", "", group_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "pickupgroup", "", group_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "namedcallgroup", "", named_groups_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "namedpickupgroup", "", named_groups_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "devicestate_busy_at", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, devicestate_busy_at));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38udptl", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.enabled));
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "t38udptl_ec", "none", t38udptl_ec_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38udptl_maxdatagram", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.t38.maxdatagram));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "faxdetect", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, faxdetect));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38udptl_nat", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.nat));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38udptl_ipv6", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.ipv6));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "tonezone", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, zone));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "language", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, language));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "recordonfeature", "automixmon", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, info.recording.onfeature));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "recordofffeature", "automixmon", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, info.recording.offfeature));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allowtransfer", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, allowtransfer));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "sdpowner", "-", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.sdpowner));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "sdpsession", "Asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.sdpsession));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "tos_audio", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.tos_audio));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "tos_video", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.tos_video));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "cos_audio", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.cos_audio));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "cos_video", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.cos_video));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allowsubscribe", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, subscription.allow));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "subminexpiry", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, subscription.minexpiry));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "subminexpirey", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, subscription.minexpiry));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fromuser", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, fromuser));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fromdomain", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, fromdomain));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mwifromuser", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, subscription.mwi.fromuser));
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtlsverify", "", dtls_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtlsrekey", "", dtls_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtlscertfile", "", dtls_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtlsprivatekey", "", dtls_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtlscipher", "", dtls_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtlscafile", "", dtls_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtlscapath", "", dtls_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtlssetup", "", dtls_handler, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "srtp_tag_32", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.srtp_tag_32));
|
||||
|
||||
if (ast_sip_initialize_sorcery_transport(sip_sorcery)) {
|
||||
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
|
||||
ast_sorcery_unref(sip_sorcery);
|
||||
sip_sorcery = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_sip_initialize_sorcery_location(sip_sorcery)) {
|
||||
ast_log(LOG_ERROR, "Failed to register SIP location support with sorcery\n");
|
||||
ast_sorcery_unref(sip_sorcery);
|
||||
sip_sorcery = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_sip_initialize_sorcery_qualify(sip_sorcery)) {
|
||||
ast_log(LOG_ERROR, "Failed to register SIP qualify support with sorcery\n");
|
||||
ast_sorcery_unref(sip_sorcery);
|
||||
sip_sorcery = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_sorcery_observer_add(sip_sorcery, "contact", &state_contact_observer);
|
||||
|
||||
if (ast_sip_initialize_sorcery_domain_alias(sip_sorcery)) {
|
||||
ast_log(LOG_ERROR, "Failed to register SIP domain aliases support with sorcery\n");
|
||||
ast_sorcery_unref(sip_sorcery);
|
||||
sip_sorcery = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_sip_initialize_sorcery_security(sip_sorcery)) {
|
||||
ast_log(LOG_ERROR, "Failed to register SIP security support\n");
|
||||
ast_sorcery_unref(sip_sorcery);
|
||||
sip_sorcery = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_sip_initialize_sorcery_global(sip_sorcery)) {
|
||||
ast_log(LOG_ERROR, "Failed to register SIP Global support\n");
|
||||
ast_sorcery_unref(sip_sorcery);
|
||||
sip_sorcery = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_sorcery_load(sip_sorcery);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ast_res_pjsip_destroy_configuration(void)
|
||||
{
|
||||
ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
|
||||
ast_sorcery_unref(sip_sorcery);
|
||||
}
|
||||
|
||||
int ast_res_pjsip_reload_configuration(void)
|
||||
{
|
||||
if (sip_sorcery) {
|
||||
ast_sorcery_reload(sip_sorcery);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void subscription_configuration_destroy(struct ast_sip_endpoint_subscription_configuration *subscription)
|
||||
{
|
||||
ast_string_field_free_memory(&subscription->mwi);
|
||||
}
|
||||
|
||||
static void info_configuration_destroy(struct ast_sip_endpoint_info_configuration *info)
|
||||
{
|
||||
ast_string_field_free_memory(&info->recording);
|
||||
}
|
||||
|
||||
static void media_configuration_destroy(struct ast_sip_endpoint_media_configuration *media)
|
||||
{
|
||||
ast_string_field_free_memory(&media->rtp);
|
||||
ast_string_field_free_memory(media);
|
||||
}
|
||||
|
||||
static void endpoint_destructor(void* obj)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
|
||||
ast_string_field_free_memory(endpoint);
|
||||
|
||||
if (endpoint->media.codecs) {
|
||||
ast_format_cap_destroy(endpoint->media.codecs);
|
||||
}
|
||||
subscription_configuration_destroy(&endpoint->subscription);
|
||||
info_configuration_destroy(&endpoint->info);
|
||||
media_configuration_destroy(&endpoint->media);
|
||||
ast_sip_auth_array_destroy(&endpoint->inbound_auths);
|
||||
ast_sip_auth_array_destroy(&endpoint->outbound_auths);
|
||||
ast_party_id_free(&endpoint->id.self);
|
||||
endpoint->pickup.named_callgroups = ast_unref_namedgroups(endpoint->pickup.named_callgroups);
|
||||
endpoint->pickup.named_pickupgroups = ast_unref_namedgroups(endpoint->pickup.named_pickupgroups);
|
||||
ao2_cleanup(endpoint->persistent);
|
||||
}
|
||||
|
||||
static int init_subscription_configuration(struct ast_sip_endpoint_subscription_configuration *subscription)
|
||||
{
|
||||
return ast_string_field_init(&subscription->mwi, 64);
|
||||
}
|
||||
|
||||
static int init_info_configuration(struct ast_sip_endpoint_info_configuration *info)
|
||||
{
|
||||
return ast_string_field_init(&info->recording, 32);
|
||||
}
|
||||
|
||||
static int init_media_configuration(struct ast_sip_endpoint_media_configuration *media)
|
||||
{
|
||||
return ast_string_field_init(media, 64) || ast_string_field_init(&media->rtp, 32);
|
||||
}
|
||||
|
||||
void *ast_sip_endpoint_alloc(const char *name)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = ast_sorcery_generic_alloc(sizeof(*endpoint), endpoint_destructor);
|
||||
if (!endpoint) {
|
||||
return NULL;
|
||||
}
|
||||
if (ast_string_field_init(endpoint, 64)) {
|
||||
ao2_cleanup(endpoint);
|
||||
return NULL;
|
||||
}
|
||||
if (!(endpoint->media.codecs = ast_format_cap_alloc_nolock())) {
|
||||
ao2_cleanup(endpoint);
|
||||
return NULL;
|
||||
}
|
||||
if (init_subscription_configuration(&endpoint->subscription)) {
|
||||
ao2_cleanup(endpoint);
|
||||
return NULL;
|
||||
}
|
||||
if (init_info_configuration(&endpoint->info)) {
|
||||
ao2_cleanup(endpoint);
|
||||
return NULL;
|
||||
}
|
||||
if (init_media_configuration(&endpoint->media)) {
|
||||
ao2_cleanup(endpoint);
|
||||
return NULL;
|
||||
}
|
||||
ast_party_id_init(&endpoint->id.self);
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
struct ao2_container *ast_sip_get_endpoints(void)
|
||||
{
|
||||
struct ao2_container *endpoints;
|
||||
|
||||
endpoints = ast_sorcery_retrieve_by_fields(sip_sorcery, "endpoint", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
|
||||
|
||||
return endpoints;
|
||||
}
|
||||
|
||||
int ast_sip_retrieve_auths(const struct ast_sip_auth_array *auths, struct ast_sip_auth **out)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < auths->num; ++i) {
|
||||
out[i] = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), SIP_SORCERY_AUTH_TYPE, auths->names[i]);
|
||||
if (!out[i]) {
|
||||
ast_log(LOG_NOTICE, "Couldn't find auth '%s'. Cannot authenticate\n", auths->names[i]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ast_sip_cleanup_auths(struct ast_sip_auth *auths[], size_t num_auths)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num_auths; ++i) {
|
||||
ao2_cleanup(auths[i]);
|
||||
}
|
||||
}
|
||||
|
||||
struct ast_sorcery *ast_sip_get_sorcery(void)
|
||||
{
|
||||
return sip_sorcery;
|
||||
}
|
322
res/res_pjsip/pjsip_distributor.c
Normal file
322
res/res_pjsip/pjsip_distributor.c
Normal file
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* 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 "asterisk/res_pjsip.h"
|
||||
|
||||
static int distribute(void *data);
|
||||
static pj_bool_t distributor(pjsip_rx_data *rdata);
|
||||
|
||||
static pjsip_module distributor_mod = {
|
||||
.name = {"Request Distributor", 19},
|
||||
.priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 6,
|
||||
.on_rx_request = distributor,
|
||||
.on_rx_response = distributor,
|
||||
};
|
||||
|
||||
/*! Dialog-specific information the distributor uses */
|
||||
struct distributor_dialog_data {
|
||||
/* Serializer to distribute tasks to for this dialog */
|
||||
struct ast_taskprocessor *serializer;
|
||||
/* Endpoint associated with this dialog */
|
||||
struct ast_sip_endpoint *endpoint;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
*
|
||||
* \note Call this with the dialog locked
|
||||
*/
|
||||
static struct distributor_dialog_data *distributor_dialog_data_alloc(pjsip_dialog *dlg)
|
||||
{
|
||||
struct distributor_dialog_data *dist;
|
||||
|
||||
dist = PJ_POOL_ZALLOC_T(dlg->pool, struct distributor_dialog_data);
|
||||
pjsip_dlg_set_mod_data(dlg, distributor_mod.id, dist);
|
||||
|
||||
return dist;
|
||||
}
|
||||
|
||||
void ast_sip_dialog_set_serializer(pjsip_dialog *dlg, struct ast_taskprocessor *serializer)
|
||||
{
|
||||
struct distributor_dialog_data *dist;
|
||||
SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock);
|
||||
|
||||
dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
|
||||
if (!dist) {
|
||||
dist = distributor_dialog_data_alloc(dlg);
|
||||
}
|
||||
dist->serializer = serializer;
|
||||
}
|
||||
|
||||
void ast_sip_dialog_set_endpoint(pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint)
|
||||
{
|
||||
struct distributor_dialog_data *dist;
|
||||
SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock);
|
||||
|
||||
dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
|
||||
if (!dist) {
|
||||
dist = distributor_dialog_data_alloc(dlg);
|
||||
}
|
||||
dist->endpoint = endpoint;
|
||||
}
|
||||
|
||||
struct ast_sip_endpoint *ast_sip_dialog_get_endpoint(pjsip_dialog *dlg)
|
||||
{
|
||||
struct distributor_dialog_data *dist;
|
||||
SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock);
|
||||
|
||||
dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
|
||||
if (!dist || !dist->endpoint) {
|
||||
return NULL;
|
||||
}
|
||||
ao2_ref(dist->endpoint, +1);
|
||||
return dist->endpoint;
|
||||
}
|
||||
|
||||
static pj_bool_t distributor(pjsip_rx_data *rdata)
|
||||
{
|
||||
pjsip_dialog *dlg = pjsip_ua_find_dialog(&rdata->msg_info.cid->id, &rdata->msg_info.to->tag, &rdata->msg_info.from->tag, PJ_TRUE);
|
||||
struct distributor_dialog_data *dist = NULL;
|
||||
struct ast_taskprocessor *serializer = NULL;
|
||||
pjsip_rx_data *clone;
|
||||
|
||||
if (dlg) {
|
||||
dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
|
||||
if (dist) {
|
||||
serializer = dist->serializer;
|
||||
}
|
||||
}
|
||||
|
||||
if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG && (
|
||||
!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) ||
|
||||
!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_bye_method)) &&
|
||||
!serializer) {
|
||||
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 481, NULL, NULL, NULL);
|
||||
goto end;
|
||||
}
|
||||
|
||||
pjsip_rx_data_clone(rdata, 0, &clone);
|
||||
|
||||
if (dist) {
|
||||
clone->endpt_info.mod_data[distributor_mod.id] = dist->endpoint;
|
||||
}
|
||||
|
||||
ast_sip_push_task(serializer, distribute, clone);
|
||||
|
||||
end:
|
||||
if (dlg) {
|
||||
pjsip_dlg_dec_lock(dlg);
|
||||
}
|
||||
|
||||
return PJ_TRUE;
|
||||
}
|
||||
|
||||
static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata);
|
||||
|
||||
static pjsip_module endpoint_mod = {
|
||||
.name = {"Endpoint Identifier", 19},
|
||||
.priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 3,
|
||||
.on_rx_request = endpoint_lookup,
|
||||
};
|
||||
|
||||
static struct ast_sip_auth *artificial_auth;
|
||||
|
||||
static int create_artificial_auth(void)
|
||||
{
|
||||
if (!(artificial_auth = ast_sorcery_alloc(
|
||||
ast_sip_get_sorcery(), SIP_SORCERY_AUTH_TYPE, "artificial"))) {
|
||||
ast_log(LOG_ERROR, "Unable to create artificial auth\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_string_field_set(artificial_auth, realm, "asterisk");
|
||||
ast_string_field_set(artificial_auth, auth_user, "");
|
||||
ast_string_field_set(artificial_auth, auth_pass, "");
|
||||
artificial_auth->type = AST_SIP_AUTH_TYPE_ARTIFICIAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ast_sip_auth *ast_sip_get_artificial_auth(void)
|
||||
{
|
||||
ao2_ref(artificial_auth, +1);
|
||||
return artificial_auth;
|
||||
}
|
||||
|
||||
static struct ast_sip_endpoint *artificial_endpoint;
|
||||
|
||||
static int create_artificial_endpoint(void)
|
||||
{
|
||||
if (!(artificial_endpoint = ast_sorcery_alloc(
|
||||
ast_sip_get_sorcery(), "endpoint", NULL))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
artificial_endpoint->inbound_auths.num = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ast_sip_endpoint *ast_sip_get_artificial_endpoint(void)
|
||||
{
|
||||
ao2_ref(artificial_endpoint, +1);
|
||||
return artificial_endpoint;
|
||||
}
|
||||
|
||||
static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint;
|
||||
int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD;
|
||||
|
||||
endpoint = rdata->endpt_info.mod_data[distributor_mod.id];
|
||||
if (endpoint) {
|
||||
/* Bumping the refcount makes refcounting consistent whether an endpoint
|
||||
* is looked up or not */
|
||||
ao2_ref(endpoint, +1);
|
||||
} else {
|
||||
endpoint = ast_sip_identify_endpoint(rdata);
|
||||
}
|
||||
|
||||
if (!endpoint && !is_ack) {
|
||||
char name[AST_UUID_STR_LEN] = "";
|
||||
pjsip_uri *from = rdata->msg_info.from->uri;
|
||||
|
||||
/* always use an artificial endpoint - per discussion no reason
|
||||
to have "alwaysauthreject" as an option. It is felt using it
|
||||
was a bug fix and it is not needed since we are not worried about
|
||||
breaking old stuff and we really don't want to enable the discovery
|
||||
of SIP accounts */
|
||||
endpoint = ast_sip_get_artificial_endpoint();
|
||||
|
||||
if (PJSIP_URI_SCHEME_IS_SIP(from) || PJSIP_URI_SCHEME_IS_SIPS(from)) {
|
||||
pjsip_sip_uri *sip_from = pjsip_uri_get_uri(from);
|
||||
ast_copy_pj_str(name, &sip_from->user, sizeof(name));
|
||||
}
|
||||
|
||||
ast_sip_report_invalid_endpoint(name, rdata);
|
||||
}
|
||||
rdata->endpt_info.mod_data[endpoint_mod.id] = endpoint;
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
static pj_bool_t authenticate(pjsip_rx_data *rdata)
|
||||
{
|
||||
RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
|
||||
int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD;
|
||||
|
||||
ast_assert(endpoint != NULL);
|
||||
|
||||
if (!is_ack && ast_sip_requires_authentication(endpoint, rdata)) {
|
||||
pjsip_tx_data *tdata;
|
||||
pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, 401, NULL, &tdata);
|
||||
switch (ast_sip_check_authentication(endpoint, rdata, tdata)) {
|
||||
case AST_SIP_AUTHENTICATION_CHALLENGE:
|
||||
/* Send the 401 we created for them */
|
||||
ast_sip_report_auth_challenge_sent(endpoint, rdata, tdata);
|
||||
pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL);
|
||||
return PJ_TRUE;
|
||||
case AST_SIP_AUTHENTICATION_SUCCESS:
|
||||
ast_sip_report_auth_success(endpoint, rdata);
|
||||
pjsip_tx_data_dec_ref(tdata);
|
||||
return PJ_FALSE;
|
||||
case AST_SIP_AUTHENTICATION_FAILED:
|
||||
ast_sip_report_auth_failed_challenge_response(endpoint, rdata);
|
||||
pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL);
|
||||
return PJ_TRUE;
|
||||
case AST_SIP_AUTHENTICATION_ERROR:
|
||||
ast_sip_report_auth_failed_challenge_response(endpoint, rdata);
|
||||
pjsip_tx_data_dec_ref(tdata);
|
||||
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
|
||||
return PJ_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
static pjsip_module auth_mod = {
|
||||
.name = {"Request Authenticator", 21},
|
||||
.priority = PJSIP_MOD_PRIORITY_APPLICATION - 1,
|
||||
.on_rx_request = authenticate,
|
||||
};
|
||||
|
||||
static int distribute(void *data)
|
||||
{
|
||||
static pjsip_process_rdata_param param = {
|
||||
.start_mod = &distributor_mod,
|
||||
.idx_after_start = 1,
|
||||
};
|
||||
pj_bool_t handled;
|
||||
pjsip_rx_data *rdata = data;
|
||||
int is_request = rdata->msg_info.msg->type == PJSIP_REQUEST_MSG;
|
||||
int is_ack = is_request ? rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD : 0;
|
||||
struct ast_sip_endpoint *endpoint;
|
||||
|
||||
pjsip_endpt_process_rx_data(ast_sip_get_pjsip_endpoint(), rdata, ¶m, &handled);
|
||||
if (!handled && is_request && !is_ack) {
|
||||
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 501, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/* The endpoint_mod stores an endpoint reference in the mod_data of rdata. This
|
||||
* is the only appropriate spot to actually decrement the reference.
|
||||
*/
|
||||
endpoint = rdata->endpt_info.mod_data[endpoint_mod.id];
|
||||
ao2_cleanup(endpoint);
|
||||
pjsip_rx_data_free_cloned(rdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ast_sip_endpoint *ast_pjsip_rdata_get_endpoint(pjsip_rx_data *rdata)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = rdata->endpt_info.mod_data[endpoint_mod.id];
|
||||
if (endpoint) {
|
||||
ao2_ref(endpoint, +1);
|
||||
}
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
int ast_sip_initialize_distributor(void)
|
||||
{
|
||||
if (create_artificial_endpoint() || create_artificial_auth()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_sip_register_service(&distributor_mod)) {
|
||||
return -1;
|
||||
}
|
||||
if (ast_sip_register_service(&endpoint_mod)) {
|
||||
return -1;
|
||||
}
|
||||
if (ast_sip_register_service(&auth_mod)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ast_sip_destroy_distributor(void)
|
||||
{
|
||||
ast_sip_unregister_service(&distributor_mod);
|
||||
ast_sip_unregister_service(&endpoint_mod);
|
||||
ast_sip_unregister_service(&auth_mod);
|
||||
|
||||
ao2_cleanup(artificial_auth);
|
||||
ao2_cleanup(artificial_endpoint);
|
||||
}
|
171
res/res_pjsip/pjsip_global_headers.c
Normal file
171
res/res_pjsip/pjsip_global_headers.c
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
#include "asterisk/res_pjsip.h"
|
||||
#include "asterisk/linkedlists.h"
|
||||
|
||||
static pj_status_t add_request_headers(pjsip_tx_data *tdata);
|
||||
static pj_status_t add_response_headers(pjsip_tx_data *tdata);
|
||||
|
||||
/*!
|
||||
* \brief Indicator we've already handled a specific request/response
|
||||
*
|
||||
* PJSIP tends to reuse requests and responses. If we already have added
|
||||
* headers to a request or response, we mark the message with this value
|
||||
* so that we know not to re-add the headers again.
|
||||
*/
|
||||
static unsigned int handled_id = 0xCA115785;
|
||||
|
||||
static pjsip_module global_header_mod = {
|
||||
.name = {"Global headers", 13},
|
||||
.priority = PJSIP_MOD_PRIORITY_APPLICATION,
|
||||
.on_tx_request = add_request_headers,
|
||||
.on_tx_response = add_response_headers,
|
||||
};
|
||||
|
||||
struct header {
|
||||
AST_DECLARE_STRING_FIELDS(
|
||||
AST_STRING_FIELD(name);
|
||||
AST_STRING_FIELD(value);
|
||||
);
|
||||
AST_LIST_ENTRY(header) next;
|
||||
};
|
||||
|
||||
static struct header *alloc_header(const char *name, const char *value)
|
||||
{
|
||||
struct header *alloc;
|
||||
|
||||
alloc = ast_calloc_with_stringfields(1, struct header, 32);
|
||||
|
||||
if (!alloc) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ast_string_field_set(alloc, name, name);
|
||||
ast_string_field_set(alloc, value, value);
|
||||
|
||||
return alloc;
|
||||
}
|
||||
|
||||
static void destroy_header(struct header *to_destroy)
|
||||
{
|
||||
ast_string_field_free_memory(to_destroy);
|
||||
ast_free(to_destroy);
|
||||
}
|
||||
|
||||
AST_RWLIST_HEAD(header_list, header);
|
||||
|
||||
static struct header_list request_headers;
|
||||
static struct header_list response_headers;
|
||||
|
||||
static void add_headers_to_message(struct header_list *headers, pjsip_tx_data *tdata)
|
||||
{
|
||||
struct header *iter;
|
||||
SCOPED_LOCK(lock, headers, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
|
||||
if (tdata->mod_data[global_header_mod.id] == &handled_id) {
|
||||
return;
|
||||
}
|
||||
AST_LIST_TRAVERSE(headers, iter, next) {
|
||||
ast_sip_add_header(tdata, iter->name, iter->value);
|
||||
};
|
||||
tdata->mod_data[global_header_mod.id] = &handled_id;
|
||||
}
|
||||
|
||||
static pj_status_t add_request_headers(pjsip_tx_data *tdata)
|
||||
{
|
||||
add_headers_to_message(&request_headers, tdata);
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
static pj_status_t add_response_headers(pjsip_tx_data *tdata)
|
||||
{
|
||||
add_headers_to_message(&response_headers, tdata);
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
static void remove_header(struct header_list *headers, const char *to_remove)
|
||||
{
|
||||
struct header *iter;
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(headers, iter, next) {
|
||||
if (!strcasecmp(iter->name, to_remove)) {
|
||||
AST_LIST_REMOVE_CURRENT(next);
|
||||
break;
|
||||
}
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
}
|
||||
|
||||
static int add_header(struct header_list *headers, const char *name, const char *value, int replace)
|
||||
{
|
||||
struct header *to_add;
|
||||
|
||||
to_add = alloc_header(name, value);
|
||||
if (!to_add) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
AST_RWLIST_WRLOCK(headers);
|
||||
if (replace) {
|
||||
remove_header(headers, name);
|
||||
}
|
||||
AST_LIST_INSERT_TAIL(headers, to_add, next);
|
||||
AST_RWLIST_UNLOCK(headers);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ast_sip_add_global_request_header(const char *name, const char *value, int replace)
|
||||
{
|
||||
return add_header(&request_headers, name, value, replace);
|
||||
}
|
||||
|
||||
int ast_sip_add_global_response_header(const char *name, const char *value, int replace)
|
||||
{
|
||||
return add_header(&response_headers, name, value, replace);
|
||||
}
|
||||
|
||||
void ast_sip_initialize_global_headers(void)
|
||||
{
|
||||
AST_RWLIST_HEAD_INIT(&request_headers);
|
||||
AST_RWLIST_HEAD_INIT(&response_headers);
|
||||
|
||||
ast_sip_register_service(&global_header_mod);
|
||||
}
|
||||
|
||||
static void destroy_headers(struct header_list *headers)
|
||||
{
|
||||
struct header *iter;
|
||||
|
||||
while ((iter = AST_RWLIST_REMOVE_HEAD(headers, next))) {
|
||||
destroy_header(iter);
|
||||
}
|
||||
AST_RWLIST_HEAD_DESTROY(headers);
|
||||
}
|
||||
|
||||
void ast_sip_destroy_global_headers(void)
|
||||
{
|
||||
destroy_headers(&request_headers);
|
||||
destroy_headers(&response_headers);
|
||||
}
|
783
res/res_pjsip/pjsip_options.c
Normal file
783
res/res_pjsip/pjsip_options.c
Normal file
@@ -0,0 +1,783 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2013, Digium, Inc.
|
||||
*
|
||||
* Matt Jordan <mjordan@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 <pjsip_ua.h>
|
||||
#include <pjlib.h>
|
||||
|
||||
#include "asterisk/res_pjsip.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/cli.h"
|
||||
#include "asterisk/time.h"
|
||||
#include "include/res_pjsip_private.h"
|
||||
|
||||
#define DEFAULT_LANGUAGE "en"
|
||||
#define DEFAULT_ENCODING "text/plain"
|
||||
#define QUALIFIED_BUCKETS 211
|
||||
|
||||
static int qualify_contact(struct ast_sip_contact *contact);
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Create a ast_sip_contact_status object.
|
||||
*/
|
||||
static void *contact_status_alloc(const char *name)
|
||||
{
|
||||
struct ast_sip_contact_status *status = ast_sorcery_generic_alloc(sizeof(*status), NULL);
|
||||
|
||||
if (!status) {
|
||||
ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
status->status = UNAVAILABLE;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Retrieve a ast_sip_contact_status object from sorcery creating
|
||||
* one if not found.
|
||||
*/
|
||||
static struct ast_sip_contact_status *find_or_create_contact_status(const struct ast_sip_contact *contact)
|
||||
{
|
||||
struct ast_sip_contact_status *status = ast_sorcery_retrieve_by_id(
|
||||
ast_sip_get_sorcery(), CONTACT_STATUS,
|
||||
ast_sorcery_object_get_id(contact));
|
||||
|
||||
if (status) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!(status = ast_sorcery_alloc(
|
||||
ast_sip_get_sorcery(), CONTACT_STATUS,
|
||||
ast_sorcery_object_get_id(contact)))) {
|
||||
|
||||
ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s\n",
|
||||
contact->uri);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ast_sorcery_create(ast_sip_get_sorcery(), status)) {
|
||||
ast_log(LOG_ERROR, "Unable to persist ast_sip_contact_status for contact %s\n",
|
||||
contact->uri);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Update an ast_sip_contact_status's elements.
|
||||
*/
|
||||
static void update_contact_status(const struct ast_sip_contact *contact,
|
||||
enum ast_sip_contact_status_type value)
|
||||
{
|
||||
RAII_VAR(struct ast_sip_contact_status *, status,
|
||||
find_or_create_contact_status(contact), ao2_cleanup);
|
||||
|
||||
RAII_VAR(struct ast_sip_contact_status *, update, ast_sorcery_alloc(
|
||||
ast_sip_get_sorcery(), CONTACT_STATUS,
|
||||
ast_sorcery_object_get_id(status)), ao2_cleanup);
|
||||
|
||||
if (!update) {
|
||||
ast_log(LOG_ERROR, "Unable to create update ast_sip_contact_status for contact %s\n",
|
||||
contact->uri);
|
||||
return;
|
||||
}
|
||||
|
||||
update->status = value;
|
||||
|
||||
/* if the contact is available calculate the rtt as
|
||||
the diff between the last start time and "now" */
|
||||
update->rtt = update->status ?
|
||||
ast_tvdiff_us(ast_tvnow(), status->rtt_start) : 0;
|
||||
|
||||
update->rtt_start = ast_tv(0, 0);
|
||||
|
||||
if (ast_sorcery_update(ast_sip_get_sorcery(), update)) {
|
||||
ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",
|
||||
contact->uri);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Initialize the start time on a contact status so the round
|
||||
* trip time can be calculated upon a valid response.
|
||||
*/
|
||||
static void init_start_time(const struct ast_sip_contact *contact)
|
||||
{
|
||||
RAII_VAR(struct ast_sip_contact_status *, status,
|
||||
find_or_create_contact_status(contact), ao2_cleanup);
|
||||
|
||||
RAII_VAR(struct ast_sip_contact_status *, update, ast_sorcery_alloc(
|
||||
ast_sip_get_sorcery(), CONTACT_STATUS,
|
||||
ast_sorcery_object_get_id(status)), ao2_cleanup);
|
||||
|
||||
if (!update) {
|
||||
ast_log(LOG_ERROR, "Unable to create update ast_sip_contact_status for contact %s\n",
|
||||
contact->uri);
|
||||
return;
|
||||
}
|
||||
|
||||
update->rtt_start = ast_tvnow();
|
||||
|
||||
if (ast_sorcery_update(ast_sip_get_sorcery(), update)) {
|
||||
ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",
|
||||
contact->uri);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief For an endpoint try to match on a given contact.
|
||||
*/
|
||||
static int on_endpoint(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
char *aor_name, *aors;
|
||||
|
||||
if (!arg || ast_strlen_zero(endpoint->aors)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
aors = ast_strdupa(endpoint->aors);
|
||||
|
||||
while ((aor_name = strsep(&aors, ","))) {
|
||||
RAII_VAR(struct ast_sip_aor *, aor,
|
||||
ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
|
||||
RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
|
||||
|
||||
if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ao2_find(contacts, arg, OBJ_NODATA | OBJ_POINTER)) {
|
||||
return CMP_MATCH;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Find endpoints associated with the given contact.
|
||||
*/
|
||||
static struct ao2_container *find_endpoints(struct ast_sip_contact *contact)
|
||||
{
|
||||
RAII_VAR(struct ao2_container *, endpoints,
|
||||
ast_sip_get_endpoints(), ao2_cleanup);
|
||||
|
||||
return ao2_callback(endpoints, OBJ_MULTIPLE, on_endpoint, contact);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Receive an response to the qualify contact request.
|
||||
*/
|
||||
static void qualify_contact_cb(void *token, pjsip_event *e)
|
||||
{
|
||||
RAII_VAR(struct ast_sip_contact *, contact, token, ao2_cleanup);
|
||||
RAII_VAR(struct ao2_container *, endpoints, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
|
||||
|
||||
pjsip_transaction *tsx = e->body.tsx_state.tsx;
|
||||
pjsip_rx_data *challenge = e->body.tsx_state.src.rdata;
|
||||
pjsip_tx_data *tdata;
|
||||
|
||||
switch(e->body.tsx_state.type) {
|
||||
case PJSIP_EVENT_TRANSPORT_ERROR:
|
||||
case PJSIP_EVENT_TIMER:
|
||||
update_contact_status(contact, UNAVAILABLE);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!contact->authenticate_qualify || (tsx->status_code != 401 &&
|
||||
tsx->status_code != 407)) {
|
||||
update_contact_status(contact, AVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
/* try to find endpoints that are associated with the contact */
|
||||
if (!(endpoints = find_endpoints(contact))) {
|
||||
ast_log(LOG_ERROR, "No endpoints found for contact %s, cannot authenticate",
|
||||
contact->uri);
|
||||
return;
|
||||
}
|
||||
|
||||
/* find "first" endpoint in order to authenticate - actually any
|
||||
endpoint should do that matched on the contact */
|
||||
endpoint = ao2_callback(endpoints, 0, NULL, NULL);
|
||||
|
||||
if (!ast_sip_create_request_with_auth(&endpoint->outbound_auths,
|
||||
challenge, tsx, &tdata)) {
|
||||
pjsip_endpt_send_request(ast_sip_get_pjsip_endpoint(), tdata,
|
||||
-1, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Attempt to qualify the contact
|
||||
*
|
||||
* \detail Sends a SIP OPTIONS request to the given contact in order to make
|
||||
* sure that contact is available.
|
||||
*/
|
||||
static int qualify_contact(struct ast_sip_contact *contact)
|
||||
{
|
||||
pjsip_tx_data *tdata;
|
||||
|
||||
if (ast_sip_create_request("OPTIONS", NULL, NULL, contact->uri, &tdata)) {
|
||||
ast_log(LOG_ERROR, "Unable to create request to qualify contact %s\n",
|
||||
contact->uri);
|
||||
return -1;
|
||||
}
|
||||
|
||||
init_start_time(contact);
|
||||
|
||||
ao2_ref(contact, +1);
|
||||
if (pjsip_endpt_send_request(ast_sip_get_pjsip_endpoint(),
|
||||
tdata, -1, contact, qualify_contact_cb) != PJ_SUCCESS) {
|
||||
pjsip_tx_data_dec_ref(tdata);
|
||||
ast_log(LOG_ERROR, "Unable to send request to qualify contact %s\n",
|
||||
contact->uri);
|
||||
ao2_ref(contact, -1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Scheduling context for sending QUALIFY request at specified intervals.
|
||||
*/
|
||||
static struct ast_sched_context *sched;
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Container to hold all actively scheduled qualifies.
|
||||
*/
|
||||
static struct ao2_container *sched_qualifies;
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Structure to hold qualify contact scheduling information.
|
||||
*/
|
||||
struct sched_data {
|
||||
/*! The scheduling id */
|
||||
int id;
|
||||
/*! The the contact being checked */
|
||||
struct ast_sip_contact *contact;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Destroy the scheduled data and remove from scheduler.
|
||||
*/
|
||||
static void sched_data_destructor(void *obj)
|
||||
{
|
||||
struct sched_data *data = obj;
|
||||
ao2_cleanup(data->contact);
|
||||
}
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Create the scheduling data object.
|
||||
*/
|
||||
static struct sched_data *sched_data_create(struct ast_sip_contact *contact)
|
||||
{
|
||||
struct sched_data *data = ao2_alloc(sizeof(*data), sched_data_destructor);
|
||||
|
||||
if (!data) {
|
||||
ast_log(LOG_ERROR, "Unable to create schedule qualify data\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data->contact = contact;
|
||||
ao2_ref(data->contact, +1);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Send a qualify contact request within a threaded task.
|
||||
*/
|
||||
static int qualify_contact_task(void *obj)
|
||||
{
|
||||
RAII_VAR(struct ast_sip_contact *, contact, obj, ao2_cleanup);
|
||||
return qualify_contact(contact);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Send a scheduled qualify contact request.
|
||||
*/
|
||||
static int qualify_contact_sched(const void *obj)
|
||||
{
|
||||
struct sched_data *data = (struct sched_data *)obj;
|
||||
|
||||
ao2_ref(data->contact, +1);
|
||||
if (ast_sip_push_task(NULL, qualify_contact_task, data->contact)) {
|
||||
ao2_ref(data->contact, -1);
|
||||
ao2_cleanup(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return data->contact->qualify_frequency * 1000;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Set up a scheduled qualify contact check.
|
||||
*/
|
||||
static void schedule_qualify(struct ast_sip_contact *contact)
|
||||
{
|
||||
RAII_VAR(struct sched_data *, data, sched_data_create(contact), ao2_cleanup);
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
ao2_ref(data, +1);
|
||||
if ((data->id = ast_sched_add_variable(
|
||||
sched, contact->qualify_frequency * 1000,
|
||||
qualify_contact_sched, data, 1)) < 0) {
|
||||
|
||||
ao2_ref(data, -1);
|
||||
ast_log(LOG_ERROR, "Unable to schedule qualify for contact %s\n",
|
||||
contact->uri);
|
||||
return;
|
||||
}
|
||||
|
||||
ao2_link(sched_qualifies, data);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Remove the contact from the scheduler.
|
||||
*/
|
||||
static void unschedule_qualify(struct ast_sip_contact *contact)
|
||||
{
|
||||
RAII_VAR(struct sched_data *, data, ao2_find(
|
||||
sched_qualifies, contact, OBJ_UNLINK), ao2_cleanup);
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
AST_SCHED_DEL_UNREF(sched, data->id, ao2_cleanup(data));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Qualify the given contact and set up scheduling if configured.
|
||||
*/
|
||||
static void qualify_and_schedule(struct ast_sip_contact *contact)
|
||||
{
|
||||
unschedule_qualify(contact);
|
||||
|
||||
if (contact->qualify_frequency) {
|
||||
ao2_ref(contact, +1);
|
||||
ast_sip_push_task(NULL, qualify_contact_task, contact);
|
||||
|
||||
schedule_qualify(contact);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief A new contact has been created make sure it is available.
|
||||
*/
|
||||
static void contact_created(const void *obj)
|
||||
{
|
||||
qualify_and_schedule((struct ast_sip_contact *)obj);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief A contact has been deleted remove status tracking.
|
||||
*/
|
||||
static void contact_deleted(const void *obj)
|
||||
{
|
||||
struct ast_sip_contact *contact = (struct ast_sip_contact *)obj;
|
||||
RAII_VAR(struct ast_sip_contact_status *, status, NULL, ao2_cleanup);
|
||||
|
||||
unschedule_qualify(contact);
|
||||
|
||||
if (!(status = ast_sorcery_retrieve_by_id(
|
||||
ast_sip_get_sorcery(), CONTACT_STATUS,
|
||||
ast_sorcery_object_get_id(contact)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ast_sorcery_delete(ast_sip_get_sorcery(), status)) {
|
||||
ast_log(LOG_ERROR, "Unable to delete ast_sip_contact_status for contact %s\n",
|
||||
contact->uri);
|
||||
}
|
||||
}
|
||||
|
||||
struct ast_sorcery_observer contact_observer = {
|
||||
.created = contact_created,
|
||||
.deleted = contact_deleted
|
||||
};
|
||||
|
||||
static pj_bool_t options_start(void)
|
||||
{
|
||||
if (!(sched = ast_sched_context_create()) ||
|
||||
ast_sched_start_thread(sched)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
static pj_bool_t options_stop(void)
|
||||
{
|
||||
ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &contact_observer);
|
||||
|
||||
ao2_t_ref(sched_qualifies, -1, "Remove scheduled qualifies on module stop");
|
||||
|
||||
if (sched) {
|
||||
ast_sched_context_destroy(sched);
|
||||
}
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
static pj_status_t send_options_response(pjsip_rx_data *rdata, int code)
|
||||
{
|
||||
pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
|
||||
pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
|
||||
pjsip_transaction *trans = pjsip_rdata_get_tsx(rdata);
|
||||
pjsip_tx_data *tdata;
|
||||
const pjsip_hdr *hdr;
|
||||
pjsip_response_addr res_addr;
|
||||
pj_status_t status;
|
||||
|
||||
/* Make the response object */
|
||||
if ((status = pjsip_endpt_create_response(
|
||||
endpt, rdata, code, NULL, &tdata) != PJ_SUCCESS)) {
|
||||
ast_log(LOG_ERROR, "Unable to create response (%d)\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Add appropriate headers */
|
||||
if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ACCEPT, NULL))) {
|
||||
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
|
||||
}
|
||||
if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ALLOW, NULL))) {
|
||||
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
|
||||
}
|
||||
if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED, NULL))) {
|
||||
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX TODO: pjsip doesn't care a lot about either of these headers -
|
||||
* while it provides specific methods to create them, they are defined
|
||||
* to be the standard string header creation. We never did add them
|
||||
* in chan_sip, although RFC 3261 says they SHOULD. Hard coded here.
|
||||
*/
|
||||
ast_sip_add_header(tdata, "Accept-Encoding", DEFAULT_ENCODING);
|
||||
ast_sip_add_header(tdata, "Accept-Language", DEFAULT_LANGUAGE);
|
||||
|
||||
if (dlg && trans) {
|
||||
status = pjsip_dlg_send_response(dlg, trans, tdata);
|
||||
} else {
|
||||
/* Get where to send request. */
|
||||
if ((status = pjsip_get_response_addr(
|
||||
tdata->pool, rdata, &res_addr)) != PJ_SUCCESS) {
|
||||
ast_log(LOG_ERROR, "Unable to get response address (%d)\n",
|
||||
status);
|
||||
|
||||
pjsip_tx_data_dec_ref(tdata);
|
||||
return status;
|
||||
}
|
||||
status = pjsip_endpt_send_response(endpt, &res_addr, tdata,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
if (status != PJ_SUCCESS) {
|
||||
ast_log(LOG_ERROR, "Unable to send response (%d)\n", status);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
|
||||
{
|
||||
RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
|
||||
pjsip_uri *ruri;
|
||||
pjsip_sip_uri *sip_ruri;
|
||||
char exten[AST_MAX_EXTENSION];
|
||||
|
||||
if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
|
||||
&pjsip_options_method)) {
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
if (!(endpoint = ast_pjsip_rdata_get_endpoint(rdata))) {
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
ruri = rdata->msg_info.msg->line.req.uri;
|
||||
if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) {
|
||||
send_options_response(rdata, 416);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sip_ruri = pjsip_uri_get_uri(ruri);
|
||||
ast_copy_pj_str(exten, &sip_ruri->user, sizeof(exten));
|
||||
|
||||
if (ast_shutting_down()) {
|
||||
send_options_response(rdata, 503);
|
||||
} else if (!ast_exists_extension(NULL, endpoint->context, exten, 1, NULL)) {
|
||||
send_options_response(rdata, 404);
|
||||
} else {
|
||||
send_options_response(rdata, 200);
|
||||
}
|
||||
return PJ_TRUE;
|
||||
}
|
||||
|
||||
static pjsip_module options_module = {
|
||||
.name = {"Options Module", 14},
|
||||
.id = -1,
|
||||
.priority = PJSIP_MOD_PRIORITY_APPLICATION,
|
||||
.start = options_start,
|
||||
.stop = options_stop,
|
||||
.on_rx_request = options_on_rx_request,
|
||||
};
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Send qualify request to the given contact.
|
||||
*/
|
||||
static int cli_on_contact(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct ast_sip_contact *contact = obj;
|
||||
struct ast_cli_args *a = arg;
|
||||
ast_cli(a->fd, " contact %s\n", contact->uri);
|
||||
qualify_contact(contact);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief For an endpoint iterate over and qualify all aors/contacts
|
||||
*/
|
||||
static void cli_qualify_contacts(struct ast_cli_args *a, const char *endpoint_name,
|
||||
struct ast_sip_endpoint *endpoint)
|
||||
{
|
||||
char *aor_name, *aors;
|
||||
|
||||
if (ast_strlen_zero(endpoint->aors)) {
|
||||
ast_cli(a->fd, "Endpoint %s has no AoR's configured\n",
|
||||
endpoint_name);
|
||||
return;
|
||||
}
|
||||
|
||||
aors = ast_strdupa(endpoint->aors);
|
||||
|
||||
while ((aor_name = strsep(&aors, ","))) {
|
||||
RAII_VAR(struct ast_sip_aor *, aor,
|
||||
ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
|
||||
RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
|
||||
|
||||
if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ast_cli(a->fd, "Sending qualify to endpoint %s\n", endpoint_name);
|
||||
ao2_callback(contacts, OBJ_NODATA, cli_on_contact, a);
|
||||
}
|
||||
}
|
||||
|
||||
static char *cli_qualify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
|
||||
const char *endpoint_name;
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "sip qualify";
|
||||
e->usage =
|
||||
"Usage: sip qualify <endpoint>\n"
|
||||
" Send a SIP OPTIONS request to all contacts on the endpoint.\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (a->argc != 3) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
endpoint_name = a->argv[2];
|
||||
|
||||
if (!(endpoint = ast_sorcery_retrieve_by_id(
|
||||
ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
|
||||
ast_cli(a->fd, "Unable to retrieve endpoint %s\n", endpoint_name);
|
||||
return CLI_FAILURE;
|
||||
}
|
||||
|
||||
/* send a qualify for all contacts registered with the endpoint */
|
||||
cli_qualify_contacts(a, endpoint_name, endpoint);
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static struct ast_cli_entry cli_options[] = {
|
||||
AST_CLI_DEFINE(cli_qualify, "Send an OPTIONS request to a SIP endpoint")
|
||||
};
|
||||
|
||||
static int sched_qualifies_hash_fn(const void *obj, int flags)
|
||||
{
|
||||
const struct sched_data *data = obj;
|
||||
|
||||
return ast_str_hash(ast_sorcery_object_get_id(data->contact));
|
||||
}
|
||||
|
||||
static int sched_qualifies_cmp_fn(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct sched_data *data = obj;
|
||||
|
||||
return !strcmp(ast_sorcery_object_get_id(data->contact),
|
||||
ast_sorcery_object_get_id(arg));
|
||||
}
|
||||
|
||||
int ast_sip_initialize_sorcery_qualify(struct ast_sorcery *sorcery)
|
||||
{
|
||||
/* initialize sorcery ast_sip_contact_status resource */
|
||||
ast_sorcery_apply_default(sorcery, CONTACT_STATUS, "memory", NULL);
|
||||
|
||||
if (ast_sorcery_object_register(sorcery, CONTACT_STATUS,
|
||||
contact_status_alloc, NULL, NULL)) {
|
||||
ast_log(LOG_ERROR, "Unable to register ast_sip_contact_status in sorcery\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_sorcery_object_field_register(sorcery, CONTACT_STATUS, "rtt", "0", OPT_UINT_T,
|
||||
1, FLDSET(struct ast_sip_contact_status, status));
|
||||
ast_sorcery_object_field_register(sorcery, CONTACT_STATUS, "rtt", "0", OPT_UINT_T,
|
||||
1, FLDSET(struct ast_sip_contact_status, rtt));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qualify_and_schedule_cb(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct ast_sip_contact *contact = obj;
|
||||
struct ast_sip_aor *aor = arg;
|
||||
|
||||
contact->qualify_frequency = aor->qualify_frequency;
|
||||
qualify_and_schedule(contact);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Qualify and schedule an endpoint's permanent contacts
|
||||
*
|
||||
* \detail For the given endpoint retrieve its list of aors, qualify all
|
||||
* permanent contacts, and schedule for checks if configured.
|
||||
*/
|
||||
static int qualify_and_schedule_permanent_cb(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct ast_sip_endpoint *endpoint = obj;
|
||||
char *aor_name, *aors;
|
||||
|
||||
if (ast_strlen_zero(endpoint->aors)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
aors = ast_strdupa(endpoint->aors);
|
||||
|
||||
while ((aor_name = strsep(&aors, ","))) {
|
||||
RAII_VAR(struct ast_sip_aor *, aor,
|
||||
ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
|
||||
|
||||
if (!aor || !aor->permanent_contacts) {
|
||||
continue;
|
||||
}
|
||||
ao2_callback(aor->permanent_contacts, OBJ_NODATA, qualify_and_schedule_cb, aor);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qualify_and_schedule_permanent(void)
|
||||
{
|
||||
RAII_VAR(struct ao2_container *, endpoints,
|
||||
ast_sip_get_endpoints(), ao2_cleanup);
|
||||
|
||||
ao2_callback(endpoints, OBJ_NODATA,
|
||||
qualify_and_schedule_permanent_cb, NULL);
|
||||
}
|
||||
|
||||
int ast_res_pjsip_init_options_handling(int reload)
|
||||
{
|
||||
const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
|
||||
|
||||
if (sched_qualifies) {
|
||||
ao2_t_ref(sched_qualifies, -1, "Remove old scheduled qualifies");
|
||||
}
|
||||
|
||||
if (!(sched_qualifies = ao2_t_container_alloc(
|
||||
QUALIFIED_BUCKETS, sched_qualifies_hash_fn, sched_qualifies_cmp_fn,
|
||||
"Create container for scheduled qualifies"))) {
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (reload) {
|
||||
qualify_and_schedule_permanent();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pjsip_endpt_register_module(ast_sip_get_pjsip_endpoint(), &options_module) != PJ_SUCCESS) {
|
||||
options_stop();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW, NULL, 1, &STR_OPTIONS) != PJ_SUCCESS) {
|
||||
pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &contact_observer)) {
|
||||
ast_log(LOG_WARNING, "Unable to add contact observer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
qualify_and_schedule_permanent();
|
||||
ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));
|
||||
|
||||
return 0;
|
||||
}
|
94
res/res_pjsip/pjsip_outbound_auth.c
Normal file
94
res/res_pjsip/pjsip_outbound_auth.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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"
|
||||
#undef bzero
|
||||
#define bzero bzero
|
||||
#include "pjsip.h"
|
||||
|
||||
#include "asterisk/res_pjsip.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "include/res_pjsip_private.h"
|
||||
|
||||
static pj_bool_t outbound_auth(pjsip_rx_data *rdata);
|
||||
|
||||
static pjsip_module outbound_auth_mod = {
|
||||
.name = {"Outbound Authentication", 19},
|
||||
.priority = PJSIP_MOD_PRIORITY_DIALOG_USAGE,
|
||||
.on_rx_response = outbound_auth,
|
||||
};
|
||||
|
||||
struct outbound_auth_cb_data {
|
||||
ast_sip_dialog_outbound_auth_cb cb;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
static pj_bool_t outbound_auth(pjsip_rx_data *rdata)
|
||||
{
|
||||
RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
|
||||
pjsip_transaction *tsx;
|
||||
pjsip_dialog *dlg;
|
||||
struct outbound_auth_cb_data *cb_data;
|
||||
pjsip_tx_data *tdata;
|
||||
|
||||
if (rdata->msg_info.msg->line.status.code != 401 &&
|
||||
rdata->msg_info.msg->line.status.code != 407) {
|
||||
/* Doesn't pertain to us. Move on */
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
tsx = pjsip_rdata_get_tsx(rdata);
|
||||
dlg = pjsip_rdata_get_dlg(rdata);
|
||||
ast_assert(dlg != NULL && tsx != NULL);
|
||||
endpoint = ast_sip_dialog_get_endpoint(dlg);
|
||||
|
||||
if (!endpoint) {
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
if (ast_sip_create_request_with_auth(&endpoint->outbound_auths, rdata, tsx, &tdata)) {
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
cb_data = dlg->mod_data[outbound_auth_mod.id];
|
||||
if (cb_data) {
|
||||
cb_data->cb(dlg, tdata, cb_data->user_data);
|
||||
return PJ_TRUE;
|
||||
}
|
||||
|
||||
pjsip_dlg_send_request(dlg, tdata, -1, NULL);
|
||||
return PJ_TRUE;
|
||||
}
|
||||
|
||||
int ast_sip_dialog_setup_outbound_authentication(pjsip_dialog *dlg, const struct ast_sip_endpoint *endpoint,
|
||||
ast_sip_dialog_outbound_auth_cb cb, void *user_data)
|
||||
{
|
||||
struct outbound_auth_cb_data *cb_data = PJ_POOL_ZALLOC_T(dlg->pool, struct outbound_auth_cb_data);
|
||||
cb_data->cb = cb;
|
||||
cb_data->user_data = user_data;
|
||||
|
||||
dlg->sess_count++;
|
||||
pjsip_dlg_add_usage(dlg, &outbound_auth_mod, cb_data);
|
||||
dlg->sess_count--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ast_sip_initialize_outbound_authentication(void) {
|
||||
return ast_sip_register_service(&outbound_auth_mod);
|
||||
}
|
234
res/res_pjsip/security_events.c
Normal file
234
res/res_pjsip/security_events.c
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2013, Digium, Inc.
|
||||
*
|
||||
* Joshua Colp <jcolp@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.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Generate security events in the PJSIP channel
|
||||
*
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <pjsip.h>
|
||||
|
||||
#include "asterisk/res_pjsip.h"
|
||||
#include "asterisk/security_events.h"
|
||||
|
||||
static int find_transport_in_use(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct ast_sip_transport *transport = obj;
|
||||
pjsip_rx_data *rdata = arg;
|
||||
|
||||
if ((transport->state->transport == rdata->tp_info.transport) ||
|
||||
(transport->state->factory && !pj_strcmp(&transport->state->factory->addr_name.host, &rdata->tp_info.transport->local_name.host) &&
|
||||
transport->state->factory->addr_name.port == rdata->tp_info.transport->local_name.port)) {
|
||||
return CMP_MATCH | CMP_STOP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum ast_transport security_event_get_transport(pjsip_rx_data *rdata)
|
||||
{
|
||||
RAII_VAR(struct ao2_container *, transports, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup);
|
||||
|
||||
/* It should be impossible for these to fail as the transport has to exist for the message to exist */
|
||||
transports = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "transport", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
|
||||
|
||||
ast_assert(transports != NULL);
|
||||
|
||||
transport = ao2_callback(transports, 0, find_transport_in_use, rdata);
|
||||
|
||||
ast_assert(transport != NULL);
|
||||
|
||||
return transport->type;
|
||||
}
|
||||
|
||||
static void security_event_populate(pjsip_rx_data *rdata, char *call_id, size_t call_id_size, struct ast_sockaddr *local, struct ast_sockaddr *remote)
|
||||
{
|
||||
char host[NI_MAXHOST];
|
||||
|
||||
ast_copy_pj_str(call_id, &rdata->msg_info.cid->id, call_id_size);
|
||||
|
||||
ast_copy_pj_str(host, &rdata->tp_info.transport->local_name.host, sizeof(host));
|
||||
ast_sockaddr_parse(local, host, PARSE_PORT_FORBID);
|
||||
ast_sockaddr_set_port(local, rdata->tp_info.transport->local_name.port);
|
||||
|
||||
ast_sockaddr_parse(remote, rdata->pkt_info.src_name, PARSE_PORT_FORBID);
|
||||
ast_sockaddr_set_port(remote, rdata->pkt_info.src_port);
|
||||
}
|
||||
|
||||
void ast_sip_report_invalid_endpoint(const char *name, pjsip_rx_data *rdata)
|
||||
{
|
||||
enum ast_transport transport = security_event_get_transport(rdata);
|
||||
char call_id[pj_strlen(&rdata->msg_info.cid->id) + 1];
|
||||
struct ast_sockaddr local, remote;
|
||||
|
||||
struct ast_security_event_inval_acct_id inval_acct_id = {
|
||||
.common.event_type = AST_SECURITY_EVENT_INVAL_ACCT_ID,
|
||||
.common.version = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION,
|
||||
.common.service = "PJSIP",
|
||||
.common.account_id = name,
|
||||
.common.local_addr = {
|
||||
.addr = &local,
|
||||
.transport = transport,
|
||||
},
|
||||
.common.remote_addr = {
|
||||
.addr = &remote,
|
||||
.transport = transport,
|
||||
},
|
||||
.common.session_id = call_id,
|
||||
};
|
||||
|
||||
security_event_populate(rdata, call_id, sizeof(call_id), &local, &remote);
|
||||
|
||||
ast_security_event_report(AST_SEC_EVT(&inval_acct_id));
|
||||
}
|
||||
|
||||
void ast_sip_report_failed_acl(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *name)
|
||||
{
|
||||
enum ast_transport transport = security_event_get_transport(rdata);
|
||||
char call_id[pj_strlen(&rdata->msg_info.cid->id) + 1];
|
||||
struct ast_sockaddr local, remote;
|
||||
|
||||
struct ast_security_event_failed_acl failed_acl_event = {
|
||||
.common.event_type = AST_SECURITY_EVENT_FAILED_ACL,
|
||||
.common.version = AST_SECURITY_EVENT_FAILED_ACL_VERSION,
|
||||
.common.service = "PJSIP",
|
||||
.common.account_id = ast_sorcery_object_get_id(endpoint),
|
||||
.common.local_addr = {
|
||||
.addr = &local,
|
||||
.transport = transport,
|
||||
},
|
||||
.common.remote_addr = {
|
||||
.addr = &remote,
|
||||
.transport = transport,
|
||||
},
|
||||
.common.session_id = call_id,
|
||||
.acl_name = name,
|
||||
};
|
||||
|
||||
security_event_populate(rdata, call_id, sizeof(call_id), &local, &remote);
|
||||
|
||||
ast_security_event_report(AST_SEC_EVT(&failed_acl_event));
|
||||
}
|
||||
|
||||
void ast_sip_report_auth_failed_challenge_response(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
|
||||
{
|
||||
pjsip_authorization_hdr *auth = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, NULL);
|
||||
enum ast_transport transport = security_event_get_transport(rdata);
|
||||
char call_id[pj_strlen(&rdata->msg_info.cid->id) + 1];
|
||||
char nonce[64] = "", response[256] = "";
|
||||
struct ast_sockaddr local, remote;
|
||||
|
||||
struct ast_security_event_chal_resp_failed chal_resp_failed = {
|
||||
.common.event_type = AST_SECURITY_EVENT_CHAL_RESP_FAILED,
|
||||
.common.version = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION,
|
||||
.common.service = "PJSIP",
|
||||
.common.account_id = ast_sorcery_object_get_id(endpoint),
|
||||
.common.local_addr = {
|
||||
.addr = &local,
|
||||
.transport = transport,
|
||||
},
|
||||
.common.remote_addr = {
|
||||
.addr = &remote,
|
||||
.transport = transport,
|
||||
},
|
||||
.common.session_id = call_id,
|
||||
|
||||
.challenge = nonce,
|
||||
.response = response,
|
||||
.expected_response = "",
|
||||
};
|
||||
|
||||
if (auth && !pj_strcmp2(&auth->scheme, "digest")) {
|
||||
ast_copy_pj_str(nonce, &auth->credential.digest.nonce, sizeof(nonce));
|
||||
ast_copy_pj_str(response, &auth->credential.digest.response, sizeof(response));
|
||||
}
|
||||
|
||||
security_event_populate(rdata, call_id, sizeof(call_id), &local, &remote);
|
||||
|
||||
ast_security_event_report(AST_SEC_EVT(&chal_resp_failed));
|
||||
}
|
||||
|
||||
void ast_sip_report_auth_success(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
|
||||
{
|
||||
pjsip_authorization_hdr *auth = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, NULL);
|
||||
enum ast_transport transport = security_event_get_transport(rdata);
|
||||
char call_id[pj_strlen(&rdata->msg_info.cid->id) + 1];
|
||||
struct ast_sockaddr local, remote;
|
||||
|
||||
struct ast_security_event_successful_auth successful_auth = {
|
||||
.common.event_type = AST_SECURITY_EVENT_SUCCESSFUL_AUTH,
|
||||
.common.version = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION,
|
||||
.common.service = "PJSIP",
|
||||
.common.account_id = ast_sorcery_object_get_id(endpoint),
|
||||
.common.local_addr = {
|
||||
.addr = &local,
|
||||
.transport = transport,
|
||||
},
|
||||
.common.remote_addr = {
|
||||
.addr = &remote,
|
||||
.transport = transport,
|
||||
},
|
||||
.common.session_id = call_id,
|
||||
.using_password = auth ? (uint32_t *)1 : (uint32_t *)0,
|
||||
};
|
||||
|
||||
security_event_populate(rdata, call_id, sizeof(call_id), &local, &remote);
|
||||
|
||||
ast_security_event_report(AST_SEC_EVT(&successful_auth));
|
||||
}
|
||||
|
||||
void ast_sip_report_auth_challenge_sent(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, pjsip_tx_data *tdata)
|
||||
{
|
||||
pjsip_www_authenticate_hdr *auth = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_WWW_AUTHENTICATE, NULL);
|
||||
enum ast_transport transport = security_event_get_transport(rdata);
|
||||
char nonce[64] = "", call_id[pj_strlen(&rdata->msg_info.cid->id) + 1];
|
||||
struct ast_sockaddr local, remote;
|
||||
|
||||
struct ast_security_event_chal_sent chal_sent = {
|
||||
.common.event_type = AST_SECURITY_EVENT_CHAL_SENT,
|
||||
.common.version = AST_SECURITY_EVENT_CHAL_SENT_VERSION,
|
||||
.common.service = "PJSIP",
|
||||
.common.account_id = ast_sorcery_object_get_id(endpoint),
|
||||
.common.local_addr = {
|
||||
.addr = &local,
|
||||
.transport = transport,
|
||||
},
|
||||
.common.remote_addr = {
|
||||
.addr = &remote,
|
||||
.transport = transport,
|
||||
},
|
||||
.common.session_id = call_id,
|
||||
.challenge = nonce,
|
||||
};
|
||||
|
||||
if (auth && !pj_strcmp2(&auth->scheme, "digest")) {
|
||||
ast_copy_pj_str(nonce, &auth->challenge.digest.nonce, sizeof(nonce));
|
||||
}
|
||||
|
||||
security_event_populate(rdata, call_id, sizeof(call_id), &local, &remote);
|
||||
|
||||
ast_security_event_report(AST_SEC_EVT(&chal_sent));
|
||||
}
|
Reference in New Issue
Block a user