diff options
-rw-r--r-- | CHANGES | 6 | ||||
-rw-r--r-- | configs/samples/pjsip.conf.sample | 2 | ||||
-rw-r--r-- | contrib/ast-db-manage/config/versions/4e2493ef32e6_add_contact_user_to_endpoint.py | 22 | ||||
-rwxr-xr-x | contrib/scripts/sip_to_pjsip/sip_to_pjsip.py | 147 | ||||
-rw-r--r-- | include/asterisk/res_pjsip.h | 2 | ||||
-rw-r--r-- | res/res_pjsip.c | 26 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_configuration.c | 27 | ||||
-rw-r--r-- | res/res_pjsip_session.c | 30 | ||||
-rw-r--r-- | rest-api-templates/api.wiki.mustache | 4 |
9 files changed, 203 insertions, 63 deletions
@@ -42,6 +42,11 @@ chan_pjsip dialplan function PJSIP_MEDIA_OFFER, this allows the formats on a PJSIP channel to be re-negotiated and updated after session set up. +res_pjsip +------------------ + * A new endpoint configuration parameter 'contact_user' has been added which + when set will override the default user set on Contact headers in outgoing + requests. ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 13 to Asterisk 14 -------------------- @@ -407,7 +412,6 @@ cdr_csv * Added a new configuration option, "newcdrcolumns", which enables use of the post-1.8 CDR columns 'peeraccount', 'linkedid', and 'sequence'. - ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 13.10.0 to Asterisk 13.11.0 ---------- ------------------------------------------------------------------------------ diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample index eac054904..0d1c03909 100644 --- a/configs/samples/pjsip.conf.sample +++ b/configs/samples/pjsip.conf.sample @@ -762,6 +762,8 @@ ;rtp_timeout_hold= ; Hang up channel if RTP is not received for the specified ; number of seconds when the channel is on hold (default: ; "0" or not enabled) +;contact_user= ; On outgoing requests, force the user portion of the Contact + ; header to this value (default: "") ;==========================AUTH SECTION OPTIONS========================= ;[auth] diff --git a/contrib/ast-db-manage/config/versions/4e2493ef32e6_add_contact_user_to_endpoint.py b/contrib/ast-db-manage/config/versions/4e2493ef32e6_add_contact_user_to_endpoint.py new file mode 100644 index 000000000..f91cff04e --- /dev/null +++ b/contrib/ast-db-manage/config/versions/4e2493ef32e6_add_contact_user_to_endpoint.py @@ -0,0 +1,22 @@ +"""Add contact_user to endpoint + +Revision ID: 4e2493ef32e6 +Revises: 3772f8f828da +Create Date: 2016-08-16 14:19:58.918466 + +""" + +# revision identifiers, used by Alembic. +revision = '4e2493ef32e6' +down_revision = '3772f8f828da' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column('ps_endpoints', sa.Column('contact_user', sa.String(80))) + + +def downgrade(): + op.drop_column('ps_endpoints', 'contact_user') diff --git a/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py b/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py index 890921673..e598caad7 100755 --- a/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py +++ b/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py @@ -54,10 +54,11 @@ def set_value(key=None, val=None, section=None, pjsip=None, def merge_value(key=None, val=None, section=None, pjsip=None, - nmapped=None, type='endpoint', section_to=None): + nmapped=None, type='endpoint', section_to=None, + key_to=None): """Merge values from the given section with those from the default.""" def _merge_value(k, v, s, r, n): - merge_value(key if key else k, v, s, r, n, type, section_to) + merge_value(key if key else k, v, s, r, n, type, section_to, key_to) # if no value or section return the merge_value # function with the enclosed key and type @@ -71,7 +72,8 @@ def merge_value(key=None, val=None, section=None, pjsip=None, sect = sip.default(section)[0] # for each merged value add it to pjsip.conf for i in sect.get_merged(key): - set_value(key, i, section_to if section_to else section, + set_value(key_to if key_to else key, i, + section_to if section_to else section, pjsip, nmapped, type) @@ -133,11 +135,14 @@ def set_timers(key, val, section, pjsip, nmapped): found in sip.conf. """ # pjsip.conf values can be yes/no, required, always + # 'required' is a new feature of chan_pjsip, which rejects + # all SIP clients not supporting Session Timers + # 'Accept' is the default value of chan_sip and maps to 'yes' + # chan_sip ignores the case, for example 'session-timers=Refuse' + val = val.lower() if val == 'originate': set_value('timers', 'always', section, pjsip, nmapped) - elif val == 'accept': - set_value('timers', 'required', section, pjsip, nmapped) - elif val == 'never': + elif val == 'refuse': set_value('timers', 'no', section, pjsip, nmapped) else: set_value('timers', 'yes', section, pjsip, nmapped) @@ -396,7 +401,7 @@ peer_map = [ ['trustpid', set_value('trust_id_inbound')], ['sendrpid', from_sendrpid], # send_pai, send_rpid ['send_diversion', set_value], - ['encrpytion', set_media_encryption], + ['encryption', set_media_encryption], ['avpf', set_value('use_avpf')], ['recordonfeature', set_record_on_feature], # automixon ['recordofffeature', set_record_off_feature], # automixon @@ -440,6 +445,9 @@ peer_map = [ ['host', from_host], # contact, max_contacts ['qualifyfreq', set_value('qualify_frequency', type='aor')], + ['maxexpiry', set_value('maximum_expiration', type='aor')], + ['minexpiry', set_value('minimum_expiration', type='aor')], + ['defaultexpiry', set_value('default_expiration', type='aor')], ############################# maps to auth##################################### # type = auth @@ -454,9 +462,9 @@ peer_map = [ ['permit', merge_value(type='acl', section_to='acl')], ['deny', merge_value(type='acl', section_to='acl')], ['acl', merge_value(type='acl', section_to='acl')], - ['contactpermit', merge_value('contact_permit', type='acl', section_to='acl')], - ['contactdeny', merge_value('contact_deny', type='acl', section_to='acl')], - ['contactacl', merge_value('contact_acl', type='acl', section_to='acl')], + ['contactpermit', merge_value(type='acl', section_to='acl', key_to='contact_permit')], + ['contactdeny', merge_value(type='acl', section_to='acl', key_to='contact_deny')], + ['contactacl', merge_value(type='acl', section_to='acl', key_to='contact_acl')], ########################### maps to transport ################################# # type = transport @@ -464,6 +472,7 @@ peer_map = [ # bind # async_operations # ca_list_file +# ca_list_path # cert_file # privkey_file # password @@ -499,21 +508,6 @@ peer_map = [ ] -def add_localnet(section, pjsip, nmapped): - """ - Adds localnet values from sip.conf's general section to a transport in - pjsip.conf. Ideally, we would have just created a template with the - localnet sections, but because this is a script, it's not hard to add - the same thing on to every transport. - """ - try: - merge_value('local_net', sip.get('general', 'localnet')[0], 'general', - pjsip, nmapped, 'transport', section) - except LookupError: - # No localnet options configured. No biggie! - pass - - def set_transport_common(section, pjsip, nmapped): """ sip.conf has several global settings that in pjsip.conf apply to individual @@ -527,21 +521,21 @@ def set_transport_common(section, pjsip, nmapped): """ try: - merge_value('local_net', sip.get('general', 'localnet')[0], 'general', - pjsip, nmapped, 'transport', section) + merge_value('localnet', sip.get('general', 'localnet')[0], 'general', + pjsip, nmapped, 'transport', section, "local_net") except LookupError: # No localnet options configured. Move on. pass try: - set_value('tos', sip.get('general', 'sip_tos')[0], 'general', pjsip, - nmapped, 'transport', section) + set_value('tos', sip.get('general', 'tos_sip')[0], section, pjsip, + nmapped, 'transport') except LookupError: pass try: - set_value('cos', sip.get('general', 'sip_cos')[0], 'general', pjsip, - nmapped, 'transport', section) + set_value('cos', sip.get('general', 'cos_sip')[0], section, pjsip, + nmapped, 'transport') except LookupError: pass @@ -697,6 +691,12 @@ def set_tls_bindaddr(val, pjsip, nmapped): set_value('bind', bind, 'transport-tls', pjsip, nmapped, 'transport') +def set_tls_cert_file(val, pjsip, section, nmapped): + """Sets cert_file based on sip.conf tlscertfile""" + set_value('cert_file', val, section, pjsip, nmapped, + 'transport') + + def set_tls_private_key(val, pjsip, nmapped): """Sets privkey_file based on sip.conf tlsprivatekey or sslprivatekey""" set_value('priv_key_file', val, 'transport-tls', pjsip, nmapped, @@ -714,6 +714,12 @@ def set_tls_cafile(val, pjsip, nmapped): 'transport') +def set_tls_capath(val, pjsip, nmapped): + """Sets ca_list_path based on sip.conf tlscapath""" + set_value('ca_list_path', val, 'transport-tls', pjsip, nmapped, + 'transport') + + def set_tls_verifyclient(val, pjsip, nmapped): """Sets verify_client based on sip.conf tlsverifyclient""" set_value('verify_client', val, 'transport-tls', pjsip, nmapped, @@ -731,11 +737,6 @@ def set_tls_verifyserver(val, pjsip, nmapped): 'transport') -def set_tls_method(val, pjsip, nmapped): - """Sets method based on sip.conf tlsclientmethod or sslclientmethod""" - set_value('method', val, 'transport-tls', pjsip, nmapped, 'transport') - - def create_tls(sip, pjsip, nmapped): """ Creates a 'transport-tls' section in pjsip.conf based on the following @@ -755,12 +756,13 @@ def create_tls(sip, pjsip, nmapped): tls_map = [ (['tlsbindaddr', 'sslbindaddr'], set_tls_bindaddr), + (['tlscertfile', 'sslcert', 'tlscert'], set_tls_cert_file), (['tlsprivatekey', 'sslprivatekey'], set_tls_private_key), (['tlscipher', 'sslcipher'], set_tls_cipher), (['tlscafile'], set_tls_cafile), + (['tlscapath', 'tlscadir'], set_tls_capath), (['tlsverifyclient'], set_tls_verifyclient), - (['tlsdontverifyserver'], set_tls_verifyserver), - (['tlsclientmethod', 'sslclientmethod'], set_tls_method) + (['tlsdontverifyserver'], set_tls_verifyserver) ] try: @@ -780,6 +782,23 @@ def create_tls(sip, pjsip, nmapped): except LookupError: pass + try: + method = sip.multi_get('general', ['tlsclientmethod', 'sslclientmethod'])[0] + print 'In chan_sip, you specified the TLS version. With chan_sip, this was just for outbound client connections. In chan_pjsip, this value is for client and server. Instead, consider not to specify \'tlsclientmethod\' for chan_sip and \'method = sslv23\' for chan_pjsip.' + except LookupError: + """ + OpenSSL emerged during the 90s. SSLv2 and SSLv3 were the only + existing methods at that time. The OpenSSL project continued. And as + of today (OpenSSL 1.0.2) this does not start SSLv2 and SSLv3 anymore + but TLSv1.0 and v1.2. Or stated differently: This method should + have been called 'method = secure' or 'method = automatic' back in + the 90s. The PJProject did not realize this and uses 'tlsv1' as + default when unspecified, which disables TLSv1.2. chan_sip used + 'sslv23' as default when unspecified, which gives TLSv1.0 and v1.2. + """ + method = 'sslv23' + set_value('method', method, 'transport-tls', pjsip, nmapped, 'transport') + set_transport_common('transport-tls', pjsip, nmapped) try: extern_addr = sip.multi_get('general', ['externaddr', 'externip', @@ -907,6 +926,17 @@ class Registration: the right of the user, then finish by using rpartition calls to remove everything to the left of the user. """ + self.peer = '' + self.protocol = 'udp' + protocols = ['udp', 'tcp', 'tls'] + for protocol in protocols: + position = user_part.find(protocol + '://') + if -1 < position: + post_transport = user_part[position + 6:] + self.peer, sep, self.protocol = user_part[:position + 3].rpartition('?') + user_part = post_transport + break + colons = user_part.count(':') if (colons == 3): # :domainport:secret:authuser @@ -927,11 +957,7 @@ class Registration: # Invalid setting raise - pre_domain, sep, self.domain = pre_auth.partition('@') - self.peer, sep, post_peer = pre_domain.rpartition('?') - transport, sep, self.user = post_peer.rpartition('://') - - self.protocol = transport if transport else 'udp' + self.user, sep, self.domain = pre_auth.partition('@') def write(self, pjsip, nmapped): """ @@ -981,9 +1007,8 @@ class Registration: if hasattr(self, 'secret') and self.secret: set_value('password', self.secret, auth_section, pjsip, nmapped, 'auth') - if hasattr(self, 'authuser'): - set_value('username', self.authuser or self.user, auth_section, - pjsip, nmapped, 'auth') + set_value('username', self.authuser if hasattr(self, 'authuser') + else self.user, auth_section, pjsip, nmapped, 'auth') set_value('outbound_auth', auth_section, section, pjsip, nmapped, 'registration') @@ -1080,6 +1105,35 @@ def find_non_mapped(sections, nmapped): pass +def map_system(sip, pjsip, nmapped): + section = 'system' # Just a label; you as user can change that + type = 'system' # Not a label, therefore not the same as section + + try: + user_agent = sip.get('general', 'useragent')[0] + set_value('user_agent', user_agent, 'global', pjsip, nmapped, 'global') + except LookupError: + pass + + try: + timer_t1 = sip.get('general', 'timert1')[0] + set_value('timer_t1', timer_t1, section, pjsip, nmapped, type) + except LookupError: + pass + + try: + timer_b = sip.get('general', 'timerb')[0] + set_value('timer_b', timer_b, section, pjsip, nmapped, type) + except LookupError: + pass + + try: + compact_headers = sip.get('general', 'compactheaders')[0] + set_value('compact_headers', compact_headers, section, pjsip, nmapped, type) + except LookupError: + pass + + def convert(sip, filename, non_mappings, include): """ Entry point for configuration file conversion. This @@ -1092,6 +1146,7 @@ def convert(sip, filename, non_mappings, include): nmapped = non_mapped(non_mappings[filename]) if not include: # Don't duplicate transport and registration configs + map_system(sip, pjsip, nmapped) map_transports(sip, pjsip, nmapped) map_registrations(sip, pjsip, nmapped) map_auth(sip, pjsip, nmapped) diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index cd6b33db4..4cede4391 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -755,6 +755,8 @@ struct ast_sip_endpoint { struct ast_acl_list *contact_acl; /*! The number of seconds into call to disable fax detection. (0 = disabled) */ unsigned int faxdetect_timeout; + /*! Override the user on the outgoing Contact header with this value. */ + char *contact_user; }; /*! diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 8a9a19db8..34edc8ca5 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -916,6 +916,12 @@ then the <replaceable>context</replaceable> setting is used. </para></description> </configOption> + <configOption name="contact_user" default=""> + <synopsis>Force the user on the outgoing Contact header to this value.</synopsis> + <description><para> + On outbound requests, force the user portion of the Contact header to this value. + </para></description> + </configOption> </configObject> <configObject name="auth"> <synopsis>Authentication type</synopsis> @@ -2865,8 +2871,16 @@ pjsip_dialog *ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint, /* Update the dialog with the new local URI, we do it afterwards so we can use the dialog pool for construction */ pj_strdup_with_null(dlg->pool, &dlg->local.info_str, &local_uri); dlg->local.info->uri = pjsip_parse_uri(dlg->pool, dlg->local.info_str.ptr, dlg->local.info_str.slen, 0); + dlg->local.contact = pjsip_parse_hdr(dlg->pool, &HCONTACT, local_uri.ptr, local_uri.slen, NULL); + if (!ast_strlen_zero(endpoint->contact_user)) { + pjsip_sip_uri *sip_uri; + + sip_uri = pjsip_uri_get_uri(dlg->local.contact->uri); + pj_strdup2(dlg->pool, &sip_uri->user, endpoint->contact_user); + } + /* If a request user has been specified and we are permitted to change it, do so */ if (!ast_strlen_zero(request_user)) { pjsip_sip_uri *sip_uri; @@ -3168,6 +3182,18 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s return -1; } + if (endpoint && !ast_strlen_zero(endpoint->contact_user)){ + pjsip_contact_hdr *contact_hdr; + pjsip_sip_uri *contact_uri; + static const pj_str_t HCONTACT = { "Contact", 7 }; + + contact_hdr = pjsip_msg_find_hdr_by_name((*tdata)->msg, &HCONTACT, NULL); + if (contact_hdr) { + contact_uri = pjsip_uri_get_uri(contact_hdr->uri); + pj_strdup2(pool, &contact_uri->user, endpoint->contact_user); + } + } + /* Add the user=phone parameter if applicable */ ast_sip_add_usereqphone(endpoint, (*tdata)->pool, (*tdata)->msg->line.req.uri); diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 9871b4186..3bced1180 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -1208,6 +1208,31 @@ static int voicemail_extension_to_str(const void *obj, const intptr_t *args, cha return 0; } +static int contact_user_handler(const struct aco_option *opt, + struct ast_variable *var, void *obj) +{ + struct ast_sip_endpoint *endpoint = obj; + + endpoint->contact_user = ast_strdup(var->value); + if (!endpoint->contact_user) { + return -1; + } + + return 0; +} + +static int contact_user_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + + *buf = ast_strdup(endpoint->contact_user); + if (!(*buf)) { + 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); @@ -1905,6 +1930,7 @@ int ast_res_pjsip_initialize_configuration(void) ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_permit", "", endpoint_acl_handler, NULL, NULL, 0, 0); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_acl", "", endpoint_acl_handler, contact_acl_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "subscribe_context", "", OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct ast_sip_endpoint, subscription.context)); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_user", "", contact_user_handler, contact_user_to_str, NULL, 0, 0); if (ast_sip_initialize_sorcery_transport()) { ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n"); @@ -2036,6 +2062,7 @@ static void endpoint_destructor(void* obj) ao2_cleanup(endpoint->persistent); ast_variables_destroy(endpoint->channel_vars); AST_VECTOR_FREE(&endpoint->ident_method_order); + ast_free(endpoint->contact_user); } static int init_subscription_configuration(struct ast_sip_endpoint_subscription_configuration *subscription) diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index 488492f4f..315393fdb 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -2773,22 +2773,21 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans } break; case PJSIP_EVENT_TRANSPORT_ERROR: - /* - * Clear the module data now to block session_inv_on_state_changed() - * from calling session_end() if it hasn't already done so. - */ - inv->mod_data[id] = NULL; + if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { + /* + * Clear the module data now to block session_inv_on_state_changed() + * from calling session_end() if it hasn't already done so. + */ + inv->mod_data[id] = NULL; - if (inv->state != PJSIP_INV_STATE_DISCONNECTED) { - session_end(session); + /* + * Pass the session ref held by session->inv_session to + * session_end_completion(). + */ + session_end_completion(session); + return; } - - /* - * Pass the session ref held by session->inv_session to - * session_end_completion(). - */ - session_end_completion(session); - return; + break; case PJSIP_EVENT_TIMER: /* * The timer event is run by the pjsip monitor thread and not @@ -2808,7 +2807,8 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans * Pass the session ref held by session->inv_session to * session_end_completion(). */ - if (ast_sip_push_task(session->serializer, session_end_completion, session)) { + if (session + && ast_sip_push_task(session->serializer, session_end_completion, session)) { /* Do it anyway even though this is not the right thread. */ session_end_completion(session); } diff --git a/rest-api-templates/api.wiki.mustache b/rest-api-templates/api.wiki.mustache index 0a54a64a7..ad12bb695 100644 --- a/rest-api-templates/api.wiki.mustache +++ b/rest-api-templates/api.wiki.mustache @@ -1,7 +1,8 @@ {{#api_declaration}} h1. {{name_title}} -|| Method || Path || Return Model || Summary || +|| Method || Path<br>h5. Parameters are case-sensitive || Return Model || Summary || + {{#apis}} {{#operations}} | {{http_method}} | [{{wiki_path}}|#{{nickname}}] | {{#response_class}}{{#is_primitive}}{{name}}{{/is_primitive}}{{^is_primitive}}[{{wiki_name}}|{{wiki_prefix}} REST Data Models#{{singular_name}}]{{/is_primitive}}{{/response_class}} | {{summary}} | @@ -17,6 +18,7 @@ h2. {{nickname}}: {{http_method}} {{wiki_path}} {{#has_path_parameters}} h3. Path parameters +Parameters are case-sensitive. {{#path_parameters}} * {{name}}: _{{data_type}}_ - {{{wiki_description}}} {{#default_value}} |