From: Mark Michelson Date: Thu, 18 Jul 2013 19:25:51 +0000 (+0000) Subject: Add a bunch of options from sip.conf to res_sip.conf X-Git-Tag: 13.0.0-beta1~1469 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c47787feab34d9572b41ebe7148c169b52362fbd;p=thirdparty%2Fasterisk.git 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 --- diff --git a/channels/chan_gulp.c b/channels/chan_gulp.c index ad50b8f4e1..beb235b75a 100644 --- a/channels/chan_gulp.c +++ b/channels/chan_gulp.c @@ -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; diff --git a/include/asterisk/res_sip.h b/include/asterisk/res_sip.h index d4f6495319..7342d9d9d9 100644 --- a/include/asterisk/res_sip.h +++ b/include/asterisk/res_sip.h @@ -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 */ diff --git a/res/res_sip.c b/res/res_sip.c index 61e8e34bea..f44a8bc665 100644 --- a/res/res_sip.c +++ b/res/res_sip.c @@ -386,6 +386,96 @@ Gulp channel driver will return busy as the device state instead of in use. + + Set which country's indications to use for channels created for this endpoint. + + + Set the default language to use for channels created for this endpoint. + + + Determines whether one-touch recording is allowed for this endpoint. + + recordonfeature + recordofffeature + + + + The feature to enact when one-touch recording is turned on. + + 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. + This setting has no effect if the endpoint's one_touch_recording option is disabled + + + one_touch_recording + recordofffeature + + + + The feature to enact when one-touch recording is turned off. + + 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. + This setting has no effect if the endpoint's one_touch_recording option is disabled + + + one_touch_recording + recordonfeature + + + + Name of the RTP engine to use for channels created for this endpoint + + + Determines whether SIP REFER transfers are allowed for this endpoint + + + String placed as the username portion of an SDP origin (o=) line. + + + String used for the SDP session (s=) line. + + + DSCP TOS bits for audio streams + + See https://wiki.asterisk.org/wiki/display/AST/IP+Quality+of+Service for more information about QoS settings + + + + DSCP TOS bits for video streams + + See https://wiki.asterisk.org/wiki/display/AST/IP+Quality+of+Service for more information about QoS settings + + + + Priority for audio streams + + See https://wiki.asterisk.org/wiki/display/AST/IP+Quality+of+Service for more information about QoS settings + + + + Priority for video streams + + See https://wiki.asterisk.org/wiki/display/AST/IP+Quality+of+Service for more information about QoS settings + + + + Determines if endpoint is allowed to initiate subscriptions with Asterisk. + + + The minimum allowed expiry time for subscriptions initiated by the endpoint. + + + Username to use in From header for requests to this endpoint. + + + Username to use in From header for unsolicited MWI NOTIFYs to this endpoint. + + + Domain to user in From header for requests to this endpoint. + Authentication type @@ -674,6 +764,48 @@ + + Options that apply to the SIP stack as well as other system-wide settings + + 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. + + + Set transaction timer T1 value (milliseconds). + + 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. + + + + Set transaction timer B value (milliseconds). + + 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. + + + + Use the short forms of common SIP header names. + + + + Options that apply globally to all SIP communications + + The settings in this section are global. Unlike options in the system + section, these options can be refreshed by performing a reload. + + + Value used in Max-Forwards header for SIP requests. + + + Value used in User-Agent header for SIP requests and Server header for SIP responses. + + ***/ @@ -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(); } diff --git a/res/res_sip.exports.in b/res/res_sip.exports.in index fb94f1ef24..3557fd6eff 100644 --- a/res/res_sip.exports.in +++ b/res/res_sip.exports.in @@ -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: *; }; diff --git a/res/res_sip/config_global.c b/res/res_sip/config_global.c new file mode 100644 index 0000000000..4c1d71a37d --- /dev/null +++ b/res/res_sip/config_global.c @@ -0,0 +1,90 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson + * + * 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 +#include + +#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; +} diff --git a/res/res_sip/config_system.c b/res/res_sip/config_system.c new file mode 100644 index 0000000000..7c5218e104 --- /dev/null +++ b/res/res_sip/config_system.c @@ -0,0 +1,112 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson + * + * 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 +#include + +#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; +} diff --git a/res/res_sip/config_transport.c b/res/res_sip/config_transport.c index b601f799c3..e5a23a107d 100644 --- a/res/res_sip/config_transport.c +++ b/res/res_sip/config_transport.c @@ -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; } diff --git a/res/res_sip/include/res_sip_private.h b/res/res_sip/include/res_sip_private.h index e5f10321c3..96a954bab2 100644 --- a/res/res_sip/include/res_sip_private.h +++ b/res/res_sip/include/res_sip_private.h @@ -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_ */ diff --git a/res/res_sip/sip_configuration.c b/res/res_sip/sip_configuration.c index ca4660acbb..5911edba50 100644 --- a/res/res_sip/sip_configuration.c +++ b/res/res_sip/sip_configuration.c @@ -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; diff --git a/res/res_sip/sip_global_headers.c b/res/res_sip/sip_global_headers.c new file mode 100644 index 0000000000..163fc0266b --- /dev/null +++ b/res/res_sip/sip_global_headers.c @@ -0,0 +1,171 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson + * + * 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 +#include + +#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); +} diff --git a/res/res_sip_caller_id.c b/res/res_sip_caller_id.c index 3a8c267710..be88606f52 100644 --- a/res/res_sip_caller_id.c +++ b/res/res_sip_caller_id.c @@ -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); + } } /*! diff --git a/res/res_sip_mwi.c b/res/res_sip_mwi.c index f18e564697..cb636dbbb6 100644 --- a/res/res_sip_mwi.c +++ b/res/res_sip_mwi.c @@ -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"; diff --git a/res/res_sip_one_touch_record_info.c b/res/res_sip_one_touch_record_info.c index b574b304c2..4e4b973554 100644 --- a/res/res_sip_one_touch_record_info.c +++ b/res/res_sip_one_touch_record_info.c @@ -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)) { diff --git a/res/res_sip_pubsub.c b/res/res_sip_pubsub.c index bc62ad5dae..2f2b524633 100644 --- a/res/res_sip_pubsub.c +++ b/res/res_sip_pubsub.c @@ -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"); diff --git a/res/res_sip_refer.c b/res/res_sip_refer.c index 9cc46442d5..48c7e6dba8 100644 --- a/res/res_sip_refer.c +++ b/res/res_sip_refer.c @@ -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); diff --git a/res/res_sip_sdp_rtp.c b/res/res_sip_sdp_rtp.c index bc150ed4a2..b299ed7583 100644 --- a/res/res_sip_sdp_rtp.c +++ b/res/res_sip_sdp_rtp.c @@ -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; } diff --git a/res/res_sip_session.c b/res/res_sip_session.c index 0b0ebe5317..8532a5a09e 100644 --- a/res/res_sip_session.c +++ b/res/res_sip_session.c @@ -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);