Add a bunch of options from sip.conf to res_sip.conf

For a complete list of the options added, see the review linked
at the bottom of this commit message.

(closes issue ASTERISK-21506)
reported by Matt Jordan

Review: https://reviewboard.asterisk.org/r/2671



git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@394759 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Mark Michelson 2013-07-18 19:25:51 +00:00
parent 3c86832f9f
commit c47787feab
17 changed files with 749 additions and 14 deletions

View File

@ -56,6 +56,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/dsp.h"
#include "asterisk/stasis_endpoints.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/indications.h"
#include "asterisk/res_sip.h"
#include "asterisk/res_sip_session.h"
@ -601,6 +602,18 @@ static struct ast_channel *gulp_new(struct ast_sip_session *session, int state,
ast_channel_named_callgroups_set(chan, session->endpoint->named_callgroups);
ast_channel_named_pickupgroups_set(chan, session->endpoint->named_pickupgroups);
if (!ast_strlen_zero(session->endpoint->language)) {
ast_channel_language_set(chan, session->endpoint->language);
}
if (!ast_strlen_zero(session->endpoint->zone)) {
struct ast_tone_zone *zone = ast_get_indication_zone(session->endpoint->zone);
if (!zone) {
ast_log(LOG_ERROR, "Unknown country code '%s' for tonezone. Check indications.conf for available country codes.\n", session->endpoint->zone);
}
ast_channel_zone_set(chan, zone);
}
ast_endpoint_add_channel(session->endpoint->persistent, chan);
return chan;

View File

@ -115,6 +115,10 @@ struct ast_sip_transport {
struct ast_sockaddr external_address;
/*! Transport state information */
struct ast_sip_transport_state *state;
/*! QOS DSCP TOS bits */
unsigned int tos;
/*! QOS COS value */
unsigned int cos;
};
/*!
@ -323,6 +327,26 @@ struct ast_sip_endpoint {
AST_STRING_FIELD(external_media_address);
/*! Configured voicemail boxes for this endpoint. Used for MWI */
AST_STRING_FIELD(mailboxes);
/*! Configured RTP engine for this endpoint. */
AST_STRING_FIELD(rtp_engine);
/*! Configured tone zone for this endpoint. */
AST_STRING_FIELD(zone);
/*! Configured language for this endpoint. */
AST_STRING_FIELD(language);
/*! Feature to enact when one-touch recording INFO with Record: On is received */
AST_STRING_FIELD(recordonfeature);
/*! Feature to enact when one-touch recording INFO with Record: Off is received */
AST_STRING_FIELD(recordofffeature);
/*! SDP origin username */
AST_STRING_FIELD(sdpowner);
/*! SDP session name */
AST_STRING_FIELD(sdpsession);
/*! Default username to place in From header */
AST_STRING_FIELD(fromuser);
/*! Domain to place in From header */
AST_STRING_FIELD(fromdomain);
/*! Username to use when sending MWI NOTIFYs to this endpoint */
AST_STRING_FIELD(mwi_from);
);
/*! Identification information for this endpoint */
struct ast_party_id id;
@ -408,6 +432,20 @@ struct ast_sip_endpoint {
struct ast_endpoint *persistent;
/*! The number of channels at which busy device state is returned */
unsigned int devicestate_busy_at;
/*! Determines if transfers (using REFER) are allowed by this endpoint */
unsigned int allowtransfer;
/*! DSCP TOS bits for audio streams */
unsigned int tos_audio;
/*! Priority for audio streams */
unsigned int cos_audio;
/*! DSCP TOS bits for video streams */
unsigned int tos_video;
/*! Priority for video streams */
unsigned int cos_video;
/*! Indicates if endpoint is allowed to initiate subscriptions */
unsigned int allowsubscribe;
/*! The minimum allowed expiration for subscriptions from endpoint */
unsigned int subminexpiry;
};
/*!
@ -1057,13 +1095,15 @@ struct ast_sip_body {
* \param endpoint Optional. If specified, the request will be created out-of-dialog
* to the endpoint.
* \param uri Optional. If specified, the request will be sent to this URI rather
* this value.
* than one configured for the endpoint.
* \param[out] tdata The newly-created request
* \retval 0 Success
* \retval -1 Failure
*/
int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg,
struct ast_sip_endpoint *endpoint, const char *uri, pjsip_tx_data **tdata);
struct ast_sip_endpoint *endpoint, const char *uri,
pjsip_tx_data **tdata);
/*!
* \brief General purpose method for sending a SIP request
@ -1308,4 +1348,12 @@ void ast_sip_report_auth_success(struct ast_sip_endpoint *endpoint, pjsip_rx_dat
*/
void ast_sip_report_auth_challenge_sent(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, pjsip_tx_data *tdata);
void ast_sip_initialize_global_headers(void);
void ast_sip_destroy_global_headers(void);
int ast_sip_add_global_request_header(const char *name, const char *value, int replace);
int ast_sip_add_global_response_header(const char *name, const char *value, int replace);
int ast_sip_initialize_sorcery_global(struct ast_sorcery *sorcery);
#endif /* _RES_SIP_H */

View File

@ -386,6 +386,96 @@
Gulp channel driver will return busy as the device state instead of in use.
</para></description>
</configOption>
<configOption name="tonezone">
<synopsis>Set which country's indications to use for channels created for this endpoint.</synopsis>
</configOption>
<configOption name="language">
<synopsis>Set the default language to use for channels created for this endpoint.</synopsis>
</configOption>
<configOption name="one_touch_recording" default="no">
<synopsis>Determines whether one-touch recording is allowed for this endpoint.</synopsis>
<see-also>
<ref type="configOption">recordonfeature</ref>
<ref type="configOption">recordofffeature</ref>
</see-also>
</configOption>
<configOption name="recordonfeature" default="automixmon">
<synopsis>The feature to enact when one-touch recording is turned on.</synopsis>
<description>
<para>When an INFO request for one-touch recording arrives with a Record header set to "on", this
feature will be enabled for the channel. The feature designated here can be any built-in
or dynamic feature defined in features.conf.</para>
<note><para>This setting has no effect if the endpoint's one_touch_recording option is disabled</para></note>
</description>
<see-also>
<ref type="configOption">one_touch_recording</ref>
<ref type="configOption">recordofffeature</ref>
</see-also>
</configOption>
<configOption name="recordofffeature" default="automixmon">
<synopsis>The feature to enact when one-touch recording is turned off.</synopsis>
<description>
<para>When an INFO request for one-touch recording arrives with a Record header set to "off", this
feature will be enabled for the channel. The feature designated here can be any built-in
or dynamic feature defined in features.conf.</para>
<note><para>This setting has no effect if the endpoint's one_touch_recording option is disabled</para></note>
</description>
<see-also>
<ref type="configOption">one_touch_recording</ref>
<ref type="configOption">recordonfeature</ref>
</see-also>
</configOption>
<configOption name="rtpengine" default="asterisk">
<synopsis>Name of the RTP engine to use for channels created for this endpoint</synopsis>
</configOption>
<configOption name="allowtransfer" default="yes">
<synopsis>Determines whether SIP REFER transfers are allowed for this endpoint</synopsis>
</configOption>
<configOption name="sdpowner" default="-">
<synopsis>String placed as the username portion of an SDP origin (o=) line.</synopsis>
</configOption>
<configOption name="sdpsession" default="Asterisk">
<synopsis>String used for the SDP session (s=) line.</synopsis>
</configOption>
<configOption name="tos_audio">
<synopsis>DSCP TOS bits for audio streams</synopsis>
<description><para>
See https://wiki.asterisk.org/wiki/display/AST/IP+Quality+of+Service for more information about QoS settings
</para></description>
</configOption>
<configOption name="tos_video">
<synopsis>DSCP TOS bits for video streams</synopsis>
<description><para>
See https://wiki.asterisk.org/wiki/display/AST/IP+Quality+of+Service for more information about QoS settings
</para></description>
</configOption>
<configOption name="cos_audio">
<synopsis>Priority for audio streams</synopsis>
<description><para>
See https://wiki.asterisk.org/wiki/display/AST/IP+Quality+of+Service for more information about QoS settings
</para></description>
</configOption>
<configOption name="cos_video">
<synopsis>Priority for video streams</synopsis>
<description><para>
See https://wiki.asterisk.org/wiki/display/AST/IP+Quality+of+Service for more information about QoS settings
</para></description>
</configOption>
<configOption name="allowsubscribe" default="no">
<synopsis>Determines if endpoint is allowed to initiate subscriptions with Asterisk.</synopsis>
</configOption>
<configOption name="subminexpiry" default="60">
<synopsis>The minimum allowed expiry time for subscriptions initiated by the endpoint.</synopsis>
</configOption>
<configOption name="fromuser">
<synopsis>Username to use in From header for requests to this endpoint.</synopsis>
</configOption>
<configOption name="mwifromuser">
<synopsis>Username to use in From header for unsolicited MWI NOTIFYs to this endpoint.</synopsis>
</configOption>
<configOption name="fromdomain">
<synopsis>Domain to user in From header for requests to this endpoint.</synopsis>
</configOption>
</configObject>
<configObject name="auth">
<synopsis>Authentication type</synopsis>
@ -674,6 +764,48 @@
</para></description>
</configOption>
</configObject>
<configObject name="system">
<synopsis>Options that apply to the SIP stack as well as other system-wide settings</synopsis>
<description><para>
The settings in this section are global. In addition to being global, the values will
not be re-evaluated when a reload is performed. This is because the values must be set
before the SIP stack is initialized. The only way to reset these values is to either
restart Asterisk, or unload res_sip.so and then load it again.
</para></description>
<configOption name="timert1" default="500">
<synopsis>Set transaction timer T1 value (milliseconds).</synopsis>
<description><para>
Timer T1 is the base for determining how long to wait before retransmitting
requests that receive no response when using an unreliable transport (e.g. UDP).
For more information on this timer, see RFC 3261, Section 17.1.1.1.
</para></description>
</configOption>
<configOption name="timerb" default="32000">
<synopsis>Set transaction timer B value (milliseconds).</synopsis>
<description><para>
Timer B determines the maximum amount of time to wait after sending an INVITE
request before terminating the transaction. It is recommended that this be set
to 64 * Timer T1, but it may be set higher if desired. For more information on
this timer, see RFC 3261, Section 17.1.1.1.
</para></description>
</configOption>
<configOption name="compactheaders" default="no">
<synopsis>Use the short forms of common SIP header names.</synopsis>
</configOption>
</configObject>
<configObject name="global">
<synopsis>Options that apply globally to all SIP communications</synopsis>
<description><para>
The settings in this section are global. Unlike options in the <literal>system</literal>
section, these options can be refreshed by performing a reload.
</para></description>
<configOption name="maxforwards" default="70">
<synopsis>Value used in Max-Forwards header for SIP requests.</synopsis>
</configOption>
<configOption name="useragent" default="Asterisk &lt;Asterisk Version&gt;">
<synopsis>Value used in User-Agent header for SIP requests and Server header for SIP responses.</synopsis>
</configOption>
</configObject>
</configFile>
</configInfo>
***/
@ -865,7 +997,7 @@ pjsip_endpoint *ast_sip_get_pjsip_endpoint(void)
return ast_pjsip_endpoint;
}
static int sip_dialog_create_from(pj_pool_t *pool, pj_str_t *from, const char *user, const pj_str_t *target, pjsip_tpselector *selector)
static int sip_dialog_create_from(pj_pool_t *pool, pj_str_t *from, const char *user, const char *domain, const pj_str_t *target, pjsip_tpselector *selector)
{
pj_str_t tmp, local_addr;
pjsip_uri *uri;
@ -874,7 +1006,7 @@ static int sip_dialog_create_from(pj_pool_t *pool, pj_str_t *from, const char *u
int local_port;
char uuid_str[AST_UUID_STR_LEN];
if (!user) {
if (ast_strlen_zero(user)) {
RAII_VAR(struct ast_uuid *, uuid, ast_uuid_generate(), ast_free_ptr);
if (!uuid) {
return -1;
@ -910,6 +1042,18 @@ static int sip_dialog_create_from(pj_pool_t *pool, pj_str_t *from, const char *u
type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
}
if (!ast_strlen_zero(domain)) {
from->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
from->slen = pj_ansi_snprintf(from->ptr, PJSIP_MAX_URL_SIZE,
"<%s:%s@%s%s%s>",
(pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE) ? "sips" : "sip",
user,
domain,
(type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? ";transport=" : "",
(type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? pjsip_transport_get_type_name(type) : "");
return 0;
}
/* Get the local bound address for the transport that will be used when communicating with the provided URI */
if (pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), pool, type, selector,
&local_addr, &local_port) != PJ_SUCCESS) {
@ -1000,7 +1144,7 @@ pjsip_dialog *ast_sip_create_dialog(const struct ast_sip_endpoint *endpoint, con
return NULL;
}
if (sip_dialog_create_from(dlg->pool, &local_uri, NULL, &remote_uri, &selector)) {
if (sip_dialog_create_from(dlg->pool, &local_uri, endpoint->fromuser, endpoint->fromdomain, &remote_uri, &selector)) {
pjsip_dlg_terminate(dlg);
return NULL;
}
@ -1127,7 +1271,8 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s
return -1;
}
if (sip_dialog_create_from(pool, &from, NULL, &remote_uri, &selector)) {
if (sip_dialog_create_from(pool, &from, endpoint ? endpoint->fromuser : NULL,
endpoint ? endpoint->fromdomain : NULL, &remote_uri, &selector)) {
ast_log(LOG_ERROR, "Unable to create From header for %.*s request to endpoint %s\n",
(int) pj_strlen(&method->name), pj_strbuf(&method->name), ast_sorcery_object_get_id(endpoint));
pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
@ -1150,7 +1295,8 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s
}
int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg,
struct ast_sip_endpoint *endpoint, const char *uri, pjsip_tx_data **tdata)
struct ast_sip_endpoint *endpoint, const char *uri,
pjsip_tx_data **tdata)
{
const pjsip_method *pmethod = get_pjsip_method(method);
@ -1443,6 +1589,18 @@ int ast_sip_thread_is_servant(void)
return *servant_id == SIP_SERVANT_ID;
}
static void remove_request_headers(pjsip_endpoint *endpt)
{
const pjsip_hdr *request_headers = pjsip_endpt_get_request_headers(endpt);
pjsip_hdr *iter = request_headers->next;
while (iter != request_headers) {
pjsip_hdr *to_erase = iter;
iter = iter->next;
pj_list_erase(to_erase);
}
}
static int load_module(void)
{
/* The third parameter is just copied from
@ -1480,12 +1638,23 @@ static int load_module(void)
ast_log(LOG_ERROR, "Failed to create PJSIP endpoint structure. Aborting load\n");
goto error;
}
/* PJSIP will automatically try to add a Max-Forwards header. Since we want to control that,
* we need to stop PJSIP from doing it automatically
*/
remove_request_headers(ast_pjsip_endpoint);
memory_pool = pj_pool_create(&caching_pool.factory, "SIP", 1024, 1024, NULL);
if (!memory_pool) {
ast_log(LOG_ERROR, "Failed to create memory pool for SIP. Aborting load\n");
goto error;
}
if (ast_sip_initialize_system()) {
ast_log(LOG_ERROR, "Failed to initialize SIP system configuration. Aborting load\n");
goto error;
}
pjsip_tsx_layer_init_module(ast_pjsip_endpoint);
pjsip_ua_init_module(ast_pjsip_endpoint, NULL);
@ -1497,6 +1666,8 @@ static int load_module(void)
goto error;
}
ast_sip_initialize_global_headers();
if (ast_res_sip_initialize_configuration()) {
ast_log(LOG_ERROR, "Failed to initialize SIP configuration. Aborting load\n");
goto error;
@ -1521,6 +1692,7 @@ return AST_MODULE_LOAD_SUCCESS;
error:
ast_sip_destroy_distributor();
ast_res_sip_destroy_configuration();
ast_sip_destroy_global_headers();
if (monitor_thread) {
stop_monitor_thread();
}
@ -1564,6 +1736,7 @@ static int unload_module(void)
{
ast_sip_destroy_distributor();
ast_res_sip_destroy_configuration();
ast_sip_destroy_global_headers();
if (monitor_thread) {
stop_monitor_thread();
}

View File

@ -60,6 +60,11 @@
LINKER_SYMBOL_PREFIXast_sip_report_auth_failed_challenge_response;
LINKER_SYMBOL_PREFIXast_sip_report_auth_success;
LINKER_SYMBOL_PREFIXast_sip_report_auth_challenge_sent;
LINKER_SYMBOL_PREFIXast_sip_initialize_global_headers;
LINKER_SYMBOL_PREFIXast_sip_destroy_global_headers;
LINKER_SYMBOL_PREFIXast_sip_add_global_request_header;
LINKER_SYMBOL_PREFIXast_sip_add_global_response_header;
LINKER_SYMBOL_PREFIXast_sip_initialize_sorcery_global;
local:
*;
};

View 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_sip.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", "res_sip.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;
}

112
res/res_sip/config_system.c Normal file
View 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_sip.h"
#include "asterisk/sorcery.h"
#include "include/res_sip_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_sip");
ast_sorcery_apply_default(system_sorcery, "system", "config", "res_sip.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;
}

View File

@ -79,6 +79,18 @@ static void *transport_alloc(const char *name)
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)
{
@ -135,12 +147,23 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
} 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) {
@ -148,9 +171,13 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
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;
}
@ -304,6 +331,8 @@ int ast_sip_initialize_sorcery_transport(struct ast_sorcery *sorcery)
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;
}

View File

@ -55,4 +55,20 @@ int ast_res_sip_init_contact_transports(void);
*/
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_ */

View File

@ -658,6 +658,24 @@ int ast_res_sip_initialize_configuration(void)
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", "rtpengine", "asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, rtp_engine));
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, recordonfeature));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "recordofffeature", "automixmon", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, recordofffeature));
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, sdpowner));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "sdpsession", "Asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, sdpsession));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "tos_audio", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, tos_audio));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "tos_video", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, tos_video));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "cos_audio", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, cos_audio));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "cos_video", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, cos_video));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allowsubscribe", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, allowsubscribe));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "subminexpiry", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, subminexpiry));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "subminexpirey", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, subminexpiry));
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, mwi_from));
if (ast_sip_initialize_sorcery_transport(sip_sorcery)) {
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
@ -696,6 +714,13 @@ int ast_res_sip_initialize_configuration(void)
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;

View 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_sip.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 = 0xB000B1E5;
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);
}

View File

@ -398,7 +398,9 @@ static void modify_id_header(pj_pool_t *pool, pjsip_fromto_hdr *id_hdr, const st
pj_strdup2(pool, &id_name_addr->display, id->name.str);
}
pj_strdup2(pool, &id_uri->user, id->number.str);
if (id->number.valid) {
pj_strdup2(pool, &id_uri->user, id->number.str);
}
}
/*!

View File

@ -281,6 +281,14 @@ static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flag
return 0;
}
if (!ast_strlen_zero(endpoint->mwi_from)) {
pjsip_fromto_hdr *from = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL);
pjsip_name_addr *from_name_addr = (pjsip_name_addr *) from->uri;
pjsip_sip_uri *from_uri = pjsip_uri_get_uri(from_name_addr->uri);
pj_strdup2(tdata->pool, &from_uri->user, endpoint->mwi_from);
}
switch (state) {
case PJSIP_EVSUB_STATE_ACTIVE:
state_name = "active";

View File

@ -51,12 +51,22 @@ static int handle_incoming_request(struct ast_sip_session *session, struct pjsip
pjsip_generic_string_hdr *record;
int feature_res;
char feature_code[AST_FEATURE_MAX_LEN];
const char *feature;
char *digit;
record = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &rec_str, NULL);
/* If we don't have Record header, we have nothing to do */
if (!record || (pj_stricmp2(&record->hvalue, "on") && pj_stricmp2(&record->hvalue, "off"))) {
if (!record) {
return 0;
}
if (!pj_stricmp2(&record->hvalue, "on")) {
feature = session->endpoint->recordonfeature;
} else if (!pj_stricmp2(&record->hvalue, "off")) {
feature = session->endpoint->recordofffeature;
} else {
/* Don't send response because another module may handle this */
return 0;
}
@ -66,13 +76,13 @@ static int handle_incoming_request(struct ast_sip_session *session, struct pjsip
}
/* Is this endpoint configured with One Touch Recording? */
if (!session->endpoint->one_touch_recording) {
if (!session->endpoint->one_touch_recording || ast_strlen_zero(feature)) {
send_response(session, 403, rdata);
return 0;
}
ast_channel_lock(session->channel);
feature_res = ast_get_builtin_feature(session->channel, "automixmon", feature_code, sizeof(feature_code));
feature_res = ast_get_feature(session->channel, feature, feature_code, sizeof(feature_code));
ast_channel_unlock(session->channel);
if (feature_res || ast_strlen_zero(feature_code)) {

View File

@ -616,6 +616,7 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
char accept[AST_SIP_MAX_ACCEPT][64];
pjsip_accept_hdr *accept_header;
pjsip_event_hdr *event_header;
pjsip_expires_hdr *expires_header;
struct ast_sip_subscription_handler *handler;
RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
struct ast_sip_subscription *sub;
@ -624,6 +625,21 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
endpoint = ast_pjsip_rdata_get_endpoint(rdata);
ast_assert(endpoint != NULL);
if (!endpoint->allowsubscribe) {
ast_log(LOG_WARNING, "Subscriptions not permitted for endpoint %s.\n", ast_sorcery_object_get_id(endpoint));
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 603, NULL, NULL, NULL);
return PJ_TRUE;
}
expires_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, rdata->msg_info.msg->hdr.next);
if (expires_header && expires_header->ivalue < endpoint->subminexpiry) {
ast_log(LOG_WARNING, "Subscription expiration %d is too brief for endpoint %s. Minimum is %d\n",
expires_header->ivalue, ast_sorcery_object_get_id(endpoint), endpoint->subminexpiry);
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 423, NULL, NULL, NULL);
return PJ_TRUE;
}
event_header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_event_name, rdata->msg_info.msg->hdr.next);
if (!event_header) {
ast_log(LOG_WARNING, "Incoming SUBSCRIBE request with no Event header\n");

View File

@ -732,6 +732,13 @@ static int refer_incoming_refer_request(struct ast_sip_session *session, struct
pjsip_param *replaces;
int response;
if (!session->endpoint->allowtransfer) {
pjsip_dlg_respond(session->inv_session->dlg, rdata, 603, NULL, NULL, NULL);
ast_log(LOG_WARNING, "Endpoint %s transfer attempt blocked due to configuration\n",
ast_sorcery_object_get_id(session->endpoint));
return 0;
}
/* A Refer-To header is required */
if (!(refer_to = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL))) {
pjsip_dlg_respond(session->inv_session->dlg, rdata, 400, NULL, NULL, NULL);

View File

@ -108,7 +108,8 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me
{
struct ast_rtp_engine_ice *ice;
if (!(session_media->rtp = ast_rtp_instance_new("asterisk", sched, ipv6 ? &address_ipv6 : &address_ipv4, NULL))) {
if (!(session_media->rtp = ast_rtp_instance_new(session->endpoint->rtp_engine, sched, ipv6 ? &address_ipv6 : &address_ipv4, NULL))) {
ast_log(LOG_ERROR, "Unable to create RTP instance using RTP engine '%s'\n", session->endpoint->rtp_engine);
return -1;
}
@ -132,6 +133,16 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me
ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND);
}
if (!strcmp(session_media->stream_type, STR_AUDIO) &&
(session->endpoint->tos_audio || session->endpoint->cos_video)) {
ast_rtp_instance_set_qos(session_media->rtp, session->endpoint->tos_audio,
session->endpoint->cos_audio, "SIP RTP Audio");
} else if (!strcmp(session_media->stream_type, STR_VIDEO) &&
(session->endpoint->tos_video || session->endpoint->cos_video)) {
ast_rtp_instance_set_qos(session_media->rtp, session->endpoint->tos_video,
session->endpoint->cos_video, "SIP RTP Video");
}
return 0;
}

View File

@ -1684,7 +1684,6 @@ static int add_sdp_streams(void *obj, void *arg, void *data, int flags)
static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, struct ast_sip_session *session, const pjmedia_sdp_session *offer)
{
RAII_VAR(struct ao2_iterator *, successful, NULL, ao2_iterator_cleanup);
static const pj_str_t STR_ASTERISK = { "Asterisk", 8 };
static const pj_str_t STR_IN = { "IN", 2 };
static const pj_str_t STR_IP4 = { "IP4", 3 };
static const pj_str_t STR_IP6 = { "IP6", 3 };
@ -1701,11 +1700,11 @@ static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, stru
local->origin.id = offer->origin.id;
}
local->origin.user = STR_ASTERISK;
pj_strdup2(inv->pool, &local->origin.user, session->endpoint->sdpowner);
local->origin.net_type = STR_IN;
local->origin.addr_type = session->endpoint->rtp_ipv6 ? STR_IP6 : STR_IP4;
local->origin.addr = *pj_gethostname();
local->name = local->origin.user;
pj_strdup2(inv->pool, &local->name, session->endpoint->sdpsession);
/* Now let the handlers add streams of various types, pjmedia will automatically reorder the media streams for us */
successful = ao2_callback_data(session->media, OBJ_MULTIPLE, add_sdp_streams, local, session);