2014-03-11 16:07:42 +00:00
|
|
|
/*
|
|
|
|
* Asterisk -- An open source telephony toolkit.
|
|
|
|
*
|
2016-09-19 11:13:21 +00:00
|
|
|
* Copyright (C) 2014-2016, Digium, Inc.
|
2014-03-11 16:07:42 +00:00
|
|
|
*
|
|
|
|
* Joshua Colp <jcolp@digium.com>
|
|
|
|
*
|
|
|
|
* See http://www.asterisk.org for more information about
|
|
|
|
* the Asterisk project. Please do not directly contact
|
|
|
|
* any of the maintainers of this project for assistance;
|
|
|
|
* the project provides a web site, mailing lists and IRC
|
|
|
|
* channels for your use.
|
|
|
|
*
|
|
|
|
* This program is free software, distributed under the terms of
|
|
|
|
* the GNU General Public License Version 2. See the LICENSE file
|
|
|
|
* at the top of the source tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "asterisk.h"
|
|
|
|
|
|
|
|
#include <pjsip.h>
|
|
|
|
#include <pjsip_ua.h>
|
|
|
|
|
|
|
|
#include "asterisk/res_pjsip.h"
|
2016-09-19 11:13:21 +00:00
|
|
|
#include "asterisk/res_pjsip_session.h"
|
|
|
|
#include "include/res_pjsip_private.h"
|
2018-11-11 16:29:10 +00:00
|
|
|
#include "asterisk/module.h"
|
2016-09-19 11:13:21 +00:00
|
|
|
|
|
|
|
#define MOD_DATA_RESTRICTIONS "restrictions"
|
|
|
|
|
2017-09-13 21:23:54 +00:00
|
|
|
static pj_status_t filter_on_tx_message(pjsip_tx_data *tdata);
|
|
|
|
static pj_bool_t filter_on_rx_message(pjsip_rx_data *rdata);
|
2016-09-19 11:13:21 +00:00
|
|
|
|
|
|
|
/*! \brief Outgoing message modification restrictions */
|
2017-09-13 21:23:54 +00:00
|
|
|
struct filter_message_restrictions {
|
2016-09-19 11:13:21 +00:00
|
|
|
/*! \brief Disallow modification of the From domain */
|
|
|
|
unsigned int disallow_from_domain_modification;
|
|
|
|
};
|
|
|
|
|
2017-09-26 16:01:48 +00:00
|
|
|
static pjsip_module filter_module_transport = {
|
|
|
|
.name = { "Message Filtering Transport", 27 },
|
2016-09-19 11:13:21 +00:00
|
|
|
.id = -1,
|
2017-09-13 21:23:54 +00:00
|
|
|
.priority = PJSIP_MOD_PRIORITY_TRANSPORT_LAYER,
|
2017-09-26 16:01:48 +00:00
|
|
|
.on_rx_request = filter_on_rx_message,
|
|
|
|
};
|
|
|
|
|
|
|
|
static pjsip_module filter_module_tsx = {
|
|
|
|
.name = { "Message Filtering TSX", 21 },
|
|
|
|
.id = -1,
|
|
|
|
.priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 1,
|
2017-09-13 21:23:54 +00:00
|
|
|
.on_tx_request = filter_on_tx_message,
|
|
|
|
.on_tx_response = filter_on_tx_message,
|
2016-09-19 11:13:21 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*! \brief Helper function to get (or allocate if not already present) restrictions on a message */
|
2017-09-13 21:23:54 +00:00
|
|
|
static struct filter_message_restrictions *get_restrictions(pjsip_tx_data *tdata)
|
2016-09-19 11:13:21 +00:00
|
|
|
{
|
2017-09-13 21:23:54 +00:00
|
|
|
struct filter_message_restrictions *restrictions;
|
2016-09-19 11:13:21 +00:00
|
|
|
|
2017-09-26 16:01:48 +00:00
|
|
|
restrictions = ast_sip_mod_data_get(tdata->mod_data, filter_module_tsx.id, MOD_DATA_RESTRICTIONS);
|
2016-09-19 11:13:21 +00:00
|
|
|
if (restrictions) {
|
|
|
|
return restrictions;
|
|
|
|
}
|
|
|
|
|
2017-09-13 21:23:54 +00:00
|
|
|
restrictions = PJ_POOL_ALLOC_T(tdata->pool, struct filter_message_restrictions);
|
2017-09-26 16:01:48 +00:00
|
|
|
ast_sip_mod_data_set(tdata->pool, tdata->mod_data, filter_module_tsx.id, MOD_DATA_RESTRICTIONS, restrictions);
|
2016-09-19 11:13:21 +00:00
|
|
|
|
|
|
|
return restrictions;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief Callback invoked on non-session outgoing messages */
|
2017-09-13 21:23:54 +00:00
|
|
|
static void filter_outgoing_message(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, struct pjsip_tx_data *tdata)
|
2016-09-19 11:13:21 +00:00
|
|
|
{
|
2017-09-13 21:23:54 +00:00
|
|
|
struct filter_message_restrictions *restrictions = get_restrictions(tdata);
|
2016-09-19 11:13:21 +00:00
|
|
|
|
|
|
|
restrictions->disallow_from_domain_modification = !ast_strlen_zero(endpoint->fromdomain);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief PJSIP Supplement for tagging messages with restrictions */
|
2017-09-13 21:23:54 +00:00
|
|
|
static struct ast_sip_supplement filter_supplement = {
|
2016-09-19 11:13:21 +00:00
|
|
|
.priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST,
|
2017-09-13 21:23:54 +00:00
|
|
|
.outgoing_request = filter_outgoing_message,
|
|
|
|
.outgoing_response = filter_outgoing_message,
|
2016-09-19 11:13:21 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*! \brief Callback invoked on session outgoing messages */
|
2017-09-13 21:23:54 +00:00
|
|
|
static void filter_session_outgoing_message(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
|
2016-09-19 11:13:21 +00:00
|
|
|
{
|
2017-09-13 21:23:54 +00:00
|
|
|
struct filter_message_restrictions *restrictions = get_restrictions(tdata);
|
2016-09-19 11:13:21 +00:00
|
|
|
|
|
|
|
restrictions->disallow_from_domain_modification = !ast_strlen_zero(session->endpoint->fromdomain);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief PJSIP Session Supplement for tagging messages with restrictions */
|
2017-09-13 21:23:54 +00:00
|
|
|
static struct ast_sip_session_supplement filter_session_supplement = {
|
2016-09-19 11:13:21 +00:00
|
|
|
.priority = 1,
|
2017-09-13 21:23:54 +00:00
|
|
|
.outgoing_request = filter_session_outgoing_message,
|
|
|
|
.outgoing_response = filter_session_outgoing_message,
|
2016-09-19 11:13:21 +00:00
|
|
|
};
|
2014-03-11 16:07:42 +00:00
|
|
|
|
|
|
|
/*! \brief Helper function which returns a UDP transport bound to the given address and port */
|
2017-09-13 21:23:54 +00:00
|
|
|
static pjsip_transport *get_udp_transport(pj_str_t *address, int port)
|
2014-03-11 16:07:42 +00:00
|
|
|
{
|
2016-01-29 23:56:42 +00:00
|
|
|
struct ao2_container *transport_states = ast_sip_get_transport_states();
|
|
|
|
struct ast_sip_transport_state *transport_state;
|
2014-03-11 16:07:42 +00:00
|
|
|
struct ao2_iterator iter;
|
|
|
|
pjsip_transport *sip_transport = NULL;
|
|
|
|
|
2016-01-29 23:56:42 +00:00
|
|
|
if (!transport_states) {
|
2014-03-11 16:07:42 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-01-29 23:56:42 +00:00
|
|
|
for (iter = ao2_iterator_init(transport_states, 0); (transport_state = ao2_iterator_next(&iter)); ao2_ref(transport_state, -1)) {
|
2019-04-09 21:47:59 +00:00
|
|
|
if (transport_state->type == AST_TRANSPORT_UDP &&
|
|
|
|
!pj_strcmp(&transport_state->transport->local_name.host, address) &&
|
|
|
|
transport_state->transport->local_name.port == port) {
|
|
|
|
sip_transport = transport_state->transport;
|
|
|
|
ao2_ref(transport_state, -1);
|
|
|
|
break;
|
2014-03-11 16:07:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ao2_iterator_destroy(&iter);
|
|
|
|
|
2016-01-29 23:56:42 +00:00
|
|
|
ao2_ref(transport_states, -1);
|
2014-03-11 16:07:42 +00:00
|
|
|
|
|
|
|
return sip_transport;
|
|
|
|
}
|
|
|
|
|
2016-09-19 11:13:21 +00:00
|
|
|
/*! \brief Helper function which determines if a transport is bound to any */
|
2017-09-13 21:23:54 +00:00
|
|
|
static int is_bound_any(pjsip_transport *transport)
|
2016-09-19 11:13:21 +00:00
|
|
|
{
|
|
|
|
pj_uint32_t loop6[4] = {0, 0, 0, 0};
|
|
|
|
|
|
|
|
if ((transport->local_addr.addr.sa_family == pj_AF_INET() &&
|
|
|
|
transport->local_addr.ipv4.sin_addr.s_addr == PJ_INADDR_ANY) ||
|
|
|
|
(transport->local_addr.addr.sa_family == pj_AF_INET6() &&
|
|
|
|
!pj_memcmp(&transport->local_addr.ipv6.sin6_addr, loop6, sizeof(loop6)))) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-11 16:07:42 +00:00
|
|
|
/*! \brief Helper function which determines if the address within SDP should be rewritten */
|
|
|
|
static int multihomed_rewrite_sdp(struct pjmedia_sdp_session *sdp)
|
|
|
|
{
|
|
|
|
if (!sdp->conn) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the host address is used in the SDP replace it with the address of what this is going out on */
|
2015-08-25 12:17:34 +00:00
|
|
|
if ((!pj_strcmp2(&sdp->conn->addr_type, "IP4") && !pj_strcmp2(&sdp->conn->addr,
|
|
|
|
ast_sip_get_host_ip_string(pj_AF_INET()))) ||
|
|
|
|
(!pj_strcmp2(&sdp->conn->addr_type, "IP6") && !pj_strcmp2(&sdp->conn->addr,
|
|
|
|
ast_sip_get_host_ip_string(pj_AF_INET6())))) {
|
2014-03-11 16:07:42 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-24 15:48:14 +00:00
|
|
|
#define is_sip_uri(uri) \
|
|
|
|
(PJSIP_URI_SCHEME_IS_SIP(uri) || PJSIP_URI_SCHEME_IS_SIPS(uri))
|
|
|
|
|
2017-09-13 21:23:54 +00:00
|
|
|
static void print_sanitize_debug(char *msg, pjsip_uri_context_e context, pjsip_sip_uri *uri)
|
|
|
|
{
|
|
|
|
#ifdef AST_DEVMODE
|
|
|
|
char hdrbuf[512];
|
|
|
|
int hdrbuf_len;
|
|
|
|
|
|
|
|
hdrbuf_len = pjsip_uri_print(context, uri, hdrbuf, 512);
|
|
|
|
hdrbuf[hdrbuf_len] = '\0';
|
|
|
|
ast_debug(2, "%s: %s\n", msg, hdrbuf);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If in DEVMODE, prevent inlining to assist in debugging */
|
2017-07-24 15:48:14 +00:00
|
|
|
#ifdef AST_DEVMODE
|
|
|
|
#define FUNC_ATTRS __attribute__ ((noinline))
|
|
|
|
#else
|
|
|
|
#define FUNC_ATTRS
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void FUNC_ATTRS sanitize_tdata(pjsip_tx_data *tdata)
|
res_pjsip: Symmetric transports
A new transport parameter 'symmetric_transport' has been added.
When a request from a dynamic contact comes in on a transport with
this option set to 'yes', the transport name will be saved and used
for subsequent outgoing requests like OPTIONS, NOTIFY and INVITE.
It's saved as a contact uri parameter named 'x-ast-txp' and will
display with the contact uri in CLI, AMI, and ARI output. On the
outgoing request, if a transport wasn't explicitly set on the
endpoint AND the request URI is not a hostname, the saved transport
will be used and the 'x-ast-txp' parameter stripped from the
outgoing packet.
* config_transport was modified to accept and store the new parameter.
* config_transport/transport_apply was updated to store the transport
name in the pjsip_transport->info field using the pjsip_transport->pool
on UDP transports.
* A 'multihomed_on_rx_message' function was added to
pjsip_message_ip_updater that, for incoming requests, retrieves the
transport name from pjsip_transport->info and retrieves the transport.
If transport->symmetric_transport is set, an 'x-ast-txp' uri parameter
containing the transport name is added to the incoming Contact header.
* An 'ast_sip_get_transport_name' function was added to res_pjsip.
It takes an ast_sip_endpoint and a pjsip_sip_uri and returns a
transport name if endpoint->transport is set or if there's an
'x-ast-txp' parameter on the uri and the uri host is an ipv4 or
ipv6 address. Otherwise it returns NULL.
* An 'ast_sip_dlg_set_transport' function was added to res_pjsip
which takes an ast_sip_endpoint, a pjsip_dialog, and an optional
pjsip_tpselector. It calls ast_sip_get_transport_name() and if
a non-NULL is returned, sets the selector and sets the transport
on the dialog. If a selector was passed in, it's updated.
* res_pjsip/ast_sip_create_dialog_uac and ast_sip_create_dialog_uas
were modified to call ast_sip_dlg_set_transport() instead of their
original logic.
* res_pjsip/create_out_of_dialog_request was modified to call
ast_sip_get_transport_name() and pjsip_tx_data_set_transport()
instead of its original logic.
* Existing transport logic was removed from endpt_send_request
since that can only be called after a create_out_of_dialog_request.
* res_pjsip/ast_sip_create_rdata was converted to a wrapper around
a new 'ast_sip_create_rdata_with_contact' function which allows
a contact_uri to be specified in addition to the existing
parameters. (See below)
* res_pjsip_pubsub/internal_pjsip_evsub_send_request was eliminated
since all it did was transport selection and that is now done in
ast_sip_create_dialog_uac and ast_sip_create_dialog_uas.
* 'contact_uri' was added to subscription_persistence. This was
necessary because although the parsed rdata contact header has the
x-ast-txp parameter added (if appropriate),
subscription_persistence_update stores the raw packet which
doesn't have it. subscription_persistence_recreate was then
updated to call ast_sip_create_rdata_with_contact with the
persisted contact_uri so the recreated subscription has the
correct transport info to send the NOTIFYs.
* res_pjsip_session/internal_pjsip_inv_send_msg was eliminated since
all it did was transport selection and that is now done in
ast_sip_create_dialog_uac.
* pjsip_message_ip_updater/multihomed_on_tx_message was updated
to remove all traces of the x-ast-txp parameter from the
outgoing headers.
NOTE: This change does NOT modify the behavior of permanent
contacts specified on an aor. To do so would require that the
permanent contact's contact uri be updated with the x-ast-txp
parameter and the aor sorcery object updated. If we need to
persue this, we need to think about cloning permanent contacts into
the same store as the dynamic ones on an aor load so they can be
updated without disturbing the originally configured value.
You CAN add the x-ast-txp parameter to a permanent contact's uri
but it would be much simpler to just set endpoint->transport.
Change-Id: I4ee1f51473da32ca54b877cd158523efcef9655f
2017-03-07 14:33:26 +00:00
|
|
|
{
|
|
|
|
static const pj_str_t x_name = { AST_SIP_X_AST_TXP, AST_SIP_X_AST_TXP_LEN };
|
|
|
|
pjsip_param *x_transport;
|
|
|
|
pjsip_sip_uri *uri;
|
|
|
|
pjsip_hdr *hdr;
|
|
|
|
|
|
|
|
if (tdata->msg->type == PJSIP_REQUEST_MSG) {
|
2017-07-24 15:48:14 +00:00
|
|
|
if (is_sip_uri(tdata->msg->line.req.uri)) {
|
|
|
|
uri = pjsip_uri_get_uri(tdata->msg->line.req.uri);
|
2017-09-13 21:23:54 +00:00
|
|
|
print_sanitize_debug("Sanitizing Request", PJSIP_URI_IN_REQ_URI, uri);
|
2017-07-24 15:48:14 +00:00
|
|
|
while ((x_transport = pjsip_param_find(&uri->other_param, &x_name))) {
|
|
|
|
pj_list_erase(x_transport);
|
|
|
|
}
|
res_pjsip: Symmetric transports
A new transport parameter 'symmetric_transport' has been added.
When a request from a dynamic contact comes in on a transport with
this option set to 'yes', the transport name will be saved and used
for subsequent outgoing requests like OPTIONS, NOTIFY and INVITE.
It's saved as a contact uri parameter named 'x-ast-txp' and will
display with the contact uri in CLI, AMI, and ARI output. On the
outgoing request, if a transport wasn't explicitly set on the
endpoint AND the request URI is not a hostname, the saved transport
will be used and the 'x-ast-txp' parameter stripped from the
outgoing packet.
* config_transport was modified to accept and store the new parameter.
* config_transport/transport_apply was updated to store the transport
name in the pjsip_transport->info field using the pjsip_transport->pool
on UDP transports.
* A 'multihomed_on_rx_message' function was added to
pjsip_message_ip_updater that, for incoming requests, retrieves the
transport name from pjsip_transport->info and retrieves the transport.
If transport->symmetric_transport is set, an 'x-ast-txp' uri parameter
containing the transport name is added to the incoming Contact header.
* An 'ast_sip_get_transport_name' function was added to res_pjsip.
It takes an ast_sip_endpoint and a pjsip_sip_uri and returns a
transport name if endpoint->transport is set or if there's an
'x-ast-txp' parameter on the uri and the uri host is an ipv4 or
ipv6 address. Otherwise it returns NULL.
* An 'ast_sip_dlg_set_transport' function was added to res_pjsip
which takes an ast_sip_endpoint, a pjsip_dialog, and an optional
pjsip_tpselector. It calls ast_sip_get_transport_name() and if
a non-NULL is returned, sets the selector and sets the transport
on the dialog. If a selector was passed in, it's updated.
* res_pjsip/ast_sip_create_dialog_uac and ast_sip_create_dialog_uas
were modified to call ast_sip_dlg_set_transport() instead of their
original logic.
* res_pjsip/create_out_of_dialog_request was modified to call
ast_sip_get_transport_name() and pjsip_tx_data_set_transport()
instead of its original logic.
* Existing transport logic was removed from endpt_send_request
since that can only be called after a create_out_of_dialog_request.
* res_pjsip/ast_sip_create_rdata was converted to a wrapper around
a new 'ast_sip_create_rdata_with_contact' function which allows
a contact_uri to be specified in addition to the existing
parameters. (See below)
* res_pjsip_pubsub/internal_pjsip_evsub_send_request was eliminated
since all it did was transport selection and that is now done in
ast_sip_create_dialog_uac and ast_sip_create_dialog_uas.
* 'contact_uri' was added to subscription_persistence. This was
necessary because although the parsed rdata contact header has the
x-ast-txp parameter added (if appropriate),
subscription_persistence_update stores the raw packet which
doesn't have it. subscription_persistence_recreate was then
updated to call ast_sip_create_rdata_with_contact with the
persisted contact_uri so the recreated subscription has the
correct transport info to send the NOTIFYs.
* res_pjsip_session/internal_pjsip_inv_send_msg was eliminated since
all it did was transport selection and that is now done in
ast_sip_create_dialog_uac.
* pjsip_message_ip_updater/multihomed_on_tx_message was updated
to remove all traces of the x-ast-txp parameter from the
outgoing headers.
NOTE: This change does NOT modify the behavior of permanent
contacts specified on an aor. To do so would require that the
permanent contact's contact uri be updated with the x-ast-txp
parameter and the aor sorcery object updated. If we need to
persue this, we need to think about cloning permanent contacts into
the same store as the dynamic ones on an aor load so they can be
updated without disturbing the originally configured value.
You CAN add the x-ast-txp parameter to a permanent contact's uri
but it would be much simpler to just set endpoint->transport.
Change-Id: I4ee1f51473da32ca54b877cd158523efcef9655f
2017-03-07 14:33:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (hdr = tdata->msg->hdr.next; hdr != &tdata->msg->hdr; hdr = hdr->next) {
|
|
|
|
if (hdr->type == PJSIP_H_TO || hdr->type == PJSIP_H_FROM) {
|
2017-09-13 21:23:54 +00:00
|
|
|
if (is_sip_uri(((pjsip_fromto_hdr *) hdr)->uri)) {
|
|
|
|
uri = pjsip_uri_get_uri(((pjsip_fromto_hdr *) hdr)->uri);
|
|
|
|
print_sanitize_debug("Sanitizing From/To header", PJSIP_URI_IN_FROMTO_HDR, uri);
|
2017-07-24 15:48:14 +00:00
|
|
|
while ((x_transport = pjsip_param_find(&uri->other_param, &x_name))) {
|
|
|
|
pj_list_erase(x_transport);
|
|
|
|
}
|
res_pjsip: Symmetric transports
A new transport parameter 'symmetric_transport' has been added.
When a request from a dynamic contact comes in on a transport with
this option set to 'yes', the transport name will be saved and used
for subsequent outgoing requests like OPTIONS, NOTIFY and INVITE.
It's saved as a contact uri parameter named 'x-ast-txp' and will
display with the contact uri in CLI, AMI, and ARI output. On the
outgoing request, if a transport wasn't explicitly set on the
endpoint AND the request URI is not a hostname, the saved transport
will be used and the 'x-ast-txp' parameter stripped from the
outgoing packet.
* config_transport was modified to accept and store the new parameter.
* config_transport/transport_apply was updated to store the transport
name in the pjsip_transport->info field using the pjsip_transport->pool
on UDP transports.
* A 'multihomed_on_rx_message' function was added to
pjsip_message_ip_updater that, for incoming requests, retrieves the
transport name from pjsip_transport->info and retrieves the transport.
If transport->symmetric_transport is set, an 'x-ast-txp' uri parameter
containing the transport name is added to the incoming Contact header.
* An 'ast_sip_get_transport_name' function was added to res_pjsip.
It takes an ast_sip_endpoint and a pjsip_sip_uri and returns a
transport name if endpoint->transport is set or if there's an
'x-ast-txp' parameter on the uri and the uri host is an ipv4 or
ipv6 address. Otherwise it returns NULL.
* An 'ast_sip_dlg_set_transport' function was added to res_pjsip
which takes an ast_sip_endpoint, a pjsip_dialog, and an optional
pjsip_tpselector. It calls ast_sip_get_transport_name() and if
a non-NULL is returned, sets the selector and sets the transport
on the dialog. If a selector was passed in, it's updated.
* res_pjsip/ast_sip_create_dialog_uac and ast_sip_create_dialog_uas
were modified to call ast_sip_dlg_set_transport() instead of their
original logic.
* res_pjsip/create_out_of_dialog_request was modified to call
ast_sip_get_transport_name() and pjsip_tx_data_set_transport()
instead of its original logic.
* Existing transport logic was removed from endpt_send_request
since that can only be called after a create_out_of_dialog_request.
* res_pjsip/ast_sip_create_rdata was converted to a wrapper around
a new 'ast_sip_create_rdata_with_contact' function which allows
a contact_uri to be specified in addition to the existing
parameters. (See below)
* res_pjsip_pubsub/internal_pjsip_evsub_send_request was eliminated
since all it did was transport selection and that is now done in
ast_sip_create_dialog_uac and ast_sip_create_dialog_uas.
* 'contact_uri' was added to subscription_persistence. This was
necessary because although the parsed rdata contact header has the
x-ast-txp parameter added (if appropriate),
subscription_persistence_update stores the raw packet which
doesn't have it. subscription_persistence_recreate was then
updated to call ast_sip_create_rdata_with_contact with the
persisted contact_uri so the recreated subscription has the
correct transport info to send the NOTIFYs.
* res_pjsip_session/internal_pjsip_inv_send_msg was eliminated since
all it did was transport selection and that is now done in
ast_sip_create_dialog_uac.
* pjsip_message_ip_updater/multihomed_on_tx_message was updated
to remove all traces of the x-ast-txp parameter from the
outgoing headers.
NOTE: This change does NOT modify the behavior of permanent
contacts specified on an aor. To do so would require that the
permanent contact's contact uri be updated with the x-ast-txp
parameter and the aor sorcery object updated. If we need to
persue this, we need to think about cloning permanent contacts into
the same store as the dynamic ones on an aor load so they can be
updated without disturbing the originally configured value.
You CAN add the x-ast-txp parameter to a permanent contact's uri
but it would be much simpler to just set endpoint->transport.
Change-Id: I4ee1f51473da32ca54b877cd158523efcef9655f
2017-03-07 14:33:26 +00:00
|
|
|
}
|
|
|
|
} else if (hdr->type == PJSIP_H_CONTACT) {
|
2017-09-13 21:23:54 +00:00
|
|
|
if (!((pjsip_contact_hdr *) hdr)->star && is_sip_uri(((pjsip_contact_hdr *) hdr)->uri)) {
|
|
|
|
uri = pjsip_uri_get_uri(((pjsip_contact_hdr *) hdr)->uri);
|
|
|
|
print_sanitize_debug("Sanitizing Contact header", PJSIP_URI_IN_CONTACT_HDR, uri);
|
2017-07-24 15:48:14 +00:00
|
|
|
while ((x_transport = pjsip_param_find(&uri->other_param, &x_name))) {
|
|
|
|
pj_list_erase(x_transport);
|
|
|
|
}
|
res_pjsip: Symmetric transports
A new transport parameter 'symmetric_transport' has been added.
When a request from a dynamic contact comes in on a transport with
this option set to 'yes', the transport name will be saved and used
for subsequent outgoing requests like OPTIONS, NOTIFY and INVITE.
It's saved as a contact uri parameter named 'x-ast-txp' and will
display with the contact uri in CLI, AMI, and ARI output. On the
outgoing request, if a transport wasn't explicitly set on the
endpoint AND the request URI is not a hostname, the saved transport
will be used and the 'x-ast-txp' parameter stripped from the
outgoing packet.
* config_transport was modified to accept and store the new parameter.
* config_transport/transport_apply was updated to store the transport
name in the pjsip_transport->info field using the pjsip_transport->pool
on UDP transports.
* A 'multihomed_on_rx_message' function was added to
pjsip_message_ip_updater that, for incoming requests, retrieves the
transport name from pjsip_transport->info and retrieves the transport.
If transport->symmetric_transport is set, an 'x-ast-txp' uri parameter
containing the transport name is added to the incoming Contact header.
* An 'ast_sip_get_transport_name' function was added to res_pjsip.
It takes an ast_sip_endpoint and a pjsip_sip_uri and returns a
transport name if endpoint->transport is set or if there's an
'x-ast-txp' parameter on the uri and the uri host is an ipv4 or
ipv6 address. Otherwise it returns NULL.
* An 'ast_sip_dlg_set_transport' function was added to res_pjsip
which takes an ast_sip_endpoint, a pjsip_dialog, and an optional
pjsip_tpselector. It calls ast_sip_get_transport_name() and if
a non-NULL is returned, sets the selector and sets the transport
on the dialog. If a selector was passed in, it's updated.
* res_pjsip/ast_sip_create_dialog_uac and ast_sip_create_dialog_uas
were modified to call ast_sip_dlg_set_transport() instead of their
original logic.
* res_pjsip/create_out_of_dialog_request was modified to call
ast_sip_get_transport_name() and pjsip_tx_data_set_transport()
instead of its original logic.
* Existing transport logic was removed from endpt_send_request
since that can only be called after a create_out_of_dialog_request.
* res_pjsip/ast_sip_create_rdata was converted to a wrapper around
a new 'ast_sip_create_rdata_with_contact' function which allows
a contact_uri to be specified in addition to the existing
parameters. (See below)
* res_pjsip_pubsub/internal_pjsip_evsub_send_request was eliminated
since all it did was transport selection and that is now done in
ast_sip_create_dialog_uac and ast_sip_create_dialog_uas.
* 'contact_uri' was added to subscription_persistence. This was
necessary because although the parsed rdata contact header has the
x-ast-txp parameter added (if appropriate),
subscription_persistence_update stores the raw packet which
doesn't have it. subscription_persistence_recreate was then
updated to call ast_sip_create_rdata_with_contact with the
persisted contact_uri so the recreated subscription has the
correct transport info to send the NOTIFYs.
* res_pjsip_session/internal_pjsip_inv_send_msg was eliminated since
all it did was transport selection and that is now done in
ast_sip_create_dialog_uac.
* pjsip_message_ip_updater/multihomed_on_tx_message was updated
to remove all traces of the x-ast-txp parameter from the
outgoing headers.
NOTE: This change does NOT modify the behavior of permanent
contacts specified on an aor. To do so would require that the
permanent contact's contact uri be updated with the x-ast-txp
parameter and the aor sorcery object updated. If we need to
persue this, we need to think about cloning permanent contacts into
the same store as the dynamic ones on an aor load so they can be
updated without disturbing the originally configured value.
You CAN add the x-ast-txp parameter to a permanent contact's uri
but it would be much simpler to just set endpoint->transport.
Change-Id: I4ee1f51473da32ca54b877cd158523efcef9655f
2017-03-07 14:33:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pjsip_tx_data_invalidate_msg(tdata);
|
|
|
|
}
|
|
|
|
|
2017-09-13 21:23:54 +00:00
|
|
|
static pj_status_t filter_on_tx_message(pjsip_tx_data *tdata)
|
2014-03-11 16:07:42 +00:00
|
|
|
{
|
2017-09-26 16:01:48 +00:00
|
|
|
struct filter_message_restrictions *restrictions =
|
|
|
|
ast_sip_mod_data_get(tdata->mod_data, filter_module_transport.id, MOD_DATA_RESTRICTIONS);
|
2014-03-11 16:07:42 +00:00
|
|
|
pjsip_tpmgr_fla2_param prm;
|
2014-03-13 13:25:09 +00:00
|
|
|
pjsip_cseq_hdr *cseq;
|
2014-03-11 16:07:42 +00:00
|
|
|
pjsip_via_hdr *via;
|
2016-09-19 11:13:21 +00:00
|
|
|
pjsip_fromto_hdr *from;
|
2014-03-11 16:07:42 +00:00
|
|
|
|
res_pjsip: Symmetric transports
A new transport parameter 'symmetric_transport' has been added.
When a request from a dynamic contact comes in on a transport with
this option set to 'yes', the transport name will be saved and used
for subsequent outgoing requests like OPTIONS, NOTIFY and INVITE.
It's saved as a contact uri parameter named 'x-ast-txp' and will
display with the contact uri in CLI, AMI, and ARI output. On the
outgoing request, if a transport wasn't explicitly set on the
endpoint AND the request URI is not a hostname, the saved transport
will be used and the 'x-ast-txp' parameter stripped from the
outgoing packet.
* config_transport was modified to accept and store the new parameter.
* config_transport/transport_apply was updated to store the transport
name in the pjsip_transport->info field using the pjsip_transport->pool
on UDP transports.
* A 'multihomed_on_rx_message' function was added to
pjsip_message_ip_updater that, for incoming requests, retrieves the
transport name from pjsip_transport->info and retrieves the transport.
If transport->symmetric_transport is set, an 'x-ast-txp' uri parameter
containing the transport name is added to the incoming Contact header.
* An 'ast_sip_get_transport_name' function was added to res_pjsip.
It takes an ast_sip_endpoint and a pjsip_sip_uri and returns a
transport name if endpoint->transport is set or if there's an
'x-ast-txp' parameter on the uri and the uri host is an ipv4 or
ipv6 address. Otherwise it returns NULL.
* An 'ast_sip_dlg_set_transport' function was added to res_pjsip
which takes an ast_sip_endpoint, a pjsip_dialog, and an optional
pjsip_tpselector. It calls ast_sip_get_transport_name() and if
a non-NULL is returned, sets the selector and sets the transport
on the dialog. If a selector was passed in, it's updated.
* res_pjsip/ast_sip_create_dialog_uac and ast_sip_create_dialog_uas
were modified to call ast_sip_dlg_set_transport() instead of their
original logic.
* res_pjsip/create_out_of_dialog_request was modified to call
ast_sip_get_transport_name() and pjsip_tx_data_set_transport()
instead of its original logic.
* Existing transport logic was removed from endpt_send_request
since that can only be called after a create_out_of_dialog_request.
* res_pjsip/ast_sip_create_rdata was converted to a wrapper around
a new 'ast_sip_create_rdata_with_contact' function which allows
a contact_uri to be specified in addition to the existing
parameters. (See below)
* res_pjsip_pubsub/internal_pjsip_evsub_send_request was eliminated
since all it did was transport selection and that is now done in
ast_sip_create_dialog_uac and ast_sip_create_dialog_uas.
* 'contact_uri' was added to subscription_persistence. This was
necessary because although the parsed rdata contact header has the
x-ast-txp parameter added (if appropriate),
subscription_persistence_update stores the raw packet which
doesn't have it. subscription_persistence_recreate was then
updated to call ast_sip_create_rdata_with_contact with the
persisted contact_uri so the recreated subscription has the
correct transport info to send the NOTIFYs.
* res_pjsip_session/internal_pjsip_inv_send_msg was eliminated since
all it did was transport selection and that is now done in
ast_sip_create_dialog_uac.
* pjsip_message_ip_updater/multihomed_on_tx_message was updated
to remove all traces of the x-ast-txp parameter from the
outgoing headers.
NOTE: This change does NOT modify the behavior of permanent
contacts specified on an aor. To do so would require that the
permanent contact's contact uri be updated with the x-ast-txp
parameter and the aor sorcery object updated. If we need to
persue this, we need to think about cloning permanent contacts into
the same store as the dynamic ones on an aor load so they can be
updated without disturbing the originally configured value.
You CAN add the x-ast-txp parameter to a permanent contact's uri
but it would be much simpler to just set endpoint->transport.
Change-Id: I4ee1f51473da32ca54b877cd158523efcef9655f
2017-03-07 14:33:26 +00:00
|
|
|
sanitize_tdata(tdata);
|
|
|
|
|
2014-03-11 16:07:42 +00:00
|
|
|
/* Use the destination information to determine what local interface this message will go out on */
|
|
|
|
pjsip_tpmgr_fla2_param_default(&prm);
|
|
|
|
prm.tp_type = tdata->tp_info.transport->key.type;
|
|
|
|
pj_strset2(&prm.dst_host, tdata->tp_info.dst_name);
|
2019-02-28 12:51:07 +00:00
|
|
|
prm.local_if = PJ_TRUE;
|
2014-03-11 16:07:42 +00:00
|
|
|
|
|
|
|
/* If we can't get the local address use best effort and let it pass */
|
|
|
|
if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), tdata->pool, &prm) != PJ_SUCCESS) {
|
|
|
|
return PJ_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2016-09-14 13:42:46 +00:00
|
|
|
/* For UDP we can have multiple transports so the port needs to be maintained */
|
|
|
|
if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP ||
|
|
|
|
tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) {
|
|
|
|
prm.ret_port = tdata->tp_info.transport->local_name.port;
|
|
|
|
}
|
2014-03-11 16:07:42 +00:00
|
|
|
|
2015-01-19 13:19:11 +00:00
|
|
|
/* If the IP source differs from the existing transport see if we need to update it */
|
|
|
|
if (pj_strcmp(&prm.ret_addr, &tdata->tp_info.transport->local_name.host)) {
|
2014-03-17 22:46:56 +00:00
|
|
|
|
2015-01-19 13:19:11 +00:00
|
|
|
/* If the transport it is going out on is different reflect it in the message */
|
|
|
|
if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP ||
|
|
|
|
tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) {
|
|
|
|
pjsip_transport *transport;
|
|
|
|
|
2017-09-13 21:23:54 +00:00
|
|
|
transport = get_udp_transport(&prm.ret_addr, prm.ret_port);
|
2015-01-19 13:19:11 +00:00
|
|
|
|
|
|
|
if (transport) {
|
|
|
|
tdata->tp_info.transport = transport;
|
|
|
|
}
|
|
|
|
}
|
2014-03-17 22:46:56 +00:00
|
|
|
|
2015-01-19 13:19:11 +00:00
|
|
|
/* If the chosen transport is not bound to any we can't use the source address as it won't get back to us */
|
2017-09-13 21:23:54 +00:00
|
|
|
if (!is_bound_any(tdata->tp_info.transport)) {
|
2015-01-19 13:19:11 +00:00
|
|
|
pj_strassign(&prm.ret_addr, &tdata->tp_info.transport->local_name.host);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* The transport chosen will deliver this but ensure it is updated with the right information */
|
|
|
|
pj_strassign(&prm.ret_addr, &tdata->tp_info.transport->local_name.host);
|
|
|
|
}
|
2014-03-17 22:46:56 +00:00
|
|
|
|
2014-03-11 16:07:42 +00:00
|
|
|
/* If the message needs to be updated with new address do so */
|
2014-03-13 13:25:09 +00:00
|
|
|
if (tdata->msg->type == PJSIP_REQUEST_MSG || !(cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL)) ||
|
|
|
|
pj_strcmp2(&cseq->method.name, "REGISTER")) {
|
|
|
|
pjsip_contact_hdr *contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
|
ARI/PJSIP: Add the ability to redirect (transfer) a channel in a Stasis app
This patch adds a new feature to ARI to redirect a channel to another server,
and fixes a few bugs in PJSIP's handling of the Transfer dialplan
application/ARI redirect capability.
*New Feature*
A new operation has been added to the ARI channels resource, redirect. With
this, a channel in a Stasis application can be redirected to another endpoint
of the same underlying channel technology.
*Bug fixes*
In the process of writing this new feature, two bugs were fixed in the PJSIP
stack:
(1) The existing .transfer channel callback had the limitation that it could
only transfer channels to a SIP URI, i.e., you had to pass
'PJSIP/sip:foo@my_provider.com' to the dialplan application. While this is
still supported, it is somewhat unintuitive - particularly in a world full
of endpoints. As such, we now also support specifying the PJSIP endpoint to
transfer to.
(2) res_pjsip_multihomed was, unfortunately, trying to 'help' a 302 redirect by
updating its Contact header. Alas, that resulted in the forwarding
destination set by the dialplan application/ARI resource/whatever being
rewritten with very incorrect information. Hence, we now don't bother
updating an outgoing response if it is a 302. Since this took a looong time
to find, some additional debug statements have been added to those modules
that update the Contact headers.
Review: https://reviewboard.asterisk.org/r/4316/
ASTERISK-24015 #close
Reported by: Private Name
ASTERISK-24703 #close
Reported by: Matt Jordan
........
Merged revisions 431717 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@431718 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2015-02-12 20:34:37 +00:00
|
|
|
if (contact && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))
|
|
|
|
&& !(tdata->msg->type == PJSIP_RESPONSE_MSG && tdata->msg->line.status.code / 100 == 3)) {
|
2014-03-13 13:25:09 +00:00
|
|
|
pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);
|
2014-03-11 16:07:42 +00:00
|
|
|
|
2015-01-19 13:19:11 +00:00
|
|
|
/* prm.ret_addr is allocated from the tdata pool OR the transport so it is perfectly fine to just do an assignment like this */
|
2014-03-13 13:25:09 +00:00
|
|
|
pj_strassign(&uri->host, &prm.ret_addr);
|
|
|
|
uri->port = prm.ret_port;
|
2017-09-26 16:01:48 +00:00
|
|
|
ast_debug(5, "Re-wrote Contact URI host/port to %.*s:%d (this may be re-written again later)\n",
|
ARI/PJSIP: Add the ability to redirect (transfer) a channel in a Stasis app
This patch adds a new feature to ARI to redirect a channel to another server,
and fixes a few bugs in PJSIP's handling of the Transfer dialplan
application/ARI redirect capability.
*New Feature*
A new operation has been added to the ARI channels resource, redirect. With
this, a channel in a Stasis application can be redirected to another endpoint
of the same underlying channel technology.
*Bug fixes*
In the process of writing this new feature, two bugs were fixed in the PJSIP
stack:
(1) The existing .transfer channel callback had the limitation that it could
only transfer channels to a SIP URI, i.e., you had to pass
'PJSIP/sip:foo@my_provider.com' to the dialplan application. While this is
still supported, it is somewhat unintuitive - particularly in a world full
of endpoints. As such, we now also support specifying the PJSIP endpoint to
transfer to.
(2) res_pjsip_multihomed was, unfortunately, trying to 'help' a 302 redirect by
updating its Contact header. Alas, that resulted in the forwarding
destination set by the dialplan application/ARI resource/whatever being
rewritten with very incorrect information. Hence, we now don't bother
updating an outgoing response if it is a 302. Since this took a looong time
to find, some additional debug statements have been added to those modules
that update the Contact headers.
Review: https://reviewboard.asterisk.org/r/4316/
ASTERISK-24015 #close
Reported by: Private Name
ASTERISK-24703 #close
Reported by: Matt Jordan
........
Merged revisions 431717 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@431718 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2015-02-12 20:34:37 +00:00
|
|
|
(int)pj_strlen(&uri->host), pj_strbuf(&uri->host), uri->port);
|
2014-03-11 16:07:42 +00:00
|
|
|
|
2016-09-19 11:13:21 +00:00
|
|
|
if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP ||
|
|
|
|
tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) {
|
|
|
|
uri->transport_param.slen = 0;
|
|
|
|
} else {
|
|
|
|
pj_strdup2(tdata->pool, &uri->transport_param, pjsip_transport_get_type_name(tdata->tp_info.transport->key.type));
|
|
|
|
}
|
|
|
|
|
2014-03-13 13:25:09 +00:00
|
|
|
pjsip_tx_data_invalidate_msg(tdata);
|
|
|
|
}
|
2014-03-11 16:07:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (tdata->msg->type == PJSIP_REQUEST_MSG && (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL))) {
|
|
|
|
pj_strassign(&via->sent_by.host, &prm.ret_addr);
|
|
|
|
via->sent_by.port = prm.ret_port;
|
|
|
|
|
|
|
|
pjsip_tx_data_invalidate_msg(tdata);
|
|
|
|
}
|
|
|
|
|
2016-09-19 11:13:21 +00:00
|
|
|
if (tdata->msg->type == PJSIP_REQUEST_MSG && (from = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL)) &&
|
|
|
|
(restrictions && !restrictions->disallow_from_domain_modification)) {
|
|
|
|
pjsip_name_addr *id_name_addr = (pjsip_name_addr *)from->uri;
|
|
|
|
pjsip_sip_uri *uri = pjsip_uri_get_uri(id_name_addr);
|
|
|
|
pj_sockaddr ip;
|
|
|
|
|
|
|
|
if (pj_strcmp2(&uri->host, "localhost") && pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &uri->host, &ip) == PJ_SUCCESS) {
|
|
|
|
pj_strassign(&uri->host, &prm.ret_addr);
|
|
|
|
pjsip_tx_data_invalidate_msg(tdata);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-11 16:07:42 +00:00
|
|
|
/* Update the SDP if it is present */
|
|
|
|
if (tdata->msg->body && ast_sip_is_content_type(&tdata->msg->body->content_type, "application", "sdp") &&
|
|
|
|
multihomed_rewrite_sdp(tdata->msg->body->data)) {
|
|
|
|
struct pjmedia_sdp_session *sdp = tdata->msg->body->data;
|
2016-09-19 11:13:21 +00:00
|
|
|
static const pj_str_t STR_IP4 = { "IP4", 3 };
|
|
|
|
static const pj_str_t STR_IP6 = { "IP6", 3 };
|
|
|
|
pj_str_t STR_IP;
|
2014-03-11 16:07:42 +00:00
|
|
|
int stream;
|
|
|
|
|
2016-09-19 11:13:21 +00:00
|
|
|
STR_IP = tdata->tp_info.transport->key.type & PJSIP_TRANSPORT_IPV6 ? STR_IP6 : STR_IP4;
|
|
|
|
|
|
|
|
pj_strassign(&sdp->origin.addr, &prm.ret_addr);
|
|
|
|
sdp->origin.addr_type = STR_IP;
|
2014-03-11 16:07:42 +00:00
|
|
|
pj_strassign(&sdp->conn->addr, &prm.ret_addr);
|
2016-09-19 11:13:21 +00:00
|
|
|
sdp->conn->addr_type = STR_IP;
|
2014-03-11 16:07:42 +00:00
|
|
|
|
|
|
|
for (stream = 0; stream < sdp->media_count; ++stream) {
|
|
|
|
if (sdp->media[stream]->conn) {
|
|
|
|
pj_strassign(&sdp->media[stream]->conn->addr, &prm.ret_addr);
|
2016-09-19 11:13:21 +00:00
|
|
|
sdp->media[stream]->conn->addr_type = STR_IP;
|
2014-03-11 16:07:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pjsip_tx_data_invalidate_msg(tdata);
|
|
|
|
}
|
|
|
|
|
|
|
|
return PJ_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2017-09-13 21:23:54 +00:00
|
|
|
enum uri_type {
|
|
|
|
URI_TYPE_REQUEST = -1,
|
|
|
|
URI_TYPE_TO = PJSIP_H_TO,
|
|
|
|
URI_TYPE_FROM = PJSIP_H_FROM,
|
|
|
|
URI_TYPE_CONTACT = PJSIP_H_CONTACT,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void print_uri_debug(enum uri_type ut, pjsip_rx_data *rdata, pjsip_hdr *hdr)
|
|
|
|
{
|
|
|
|
#ifdef AST_DEVMODE
|
|
|
|
pjsip_uri *local_uri = NULL;
|
|
|
|
char hdrbuf[512];
|
|
|
|
int hdrbuf_len;
|
|
|
|
char *request_uri;
|
|
|
|
pjsip_uri_context_e context = PJSIP_URI_IN_OTHER;
|
|
|
|
char header_name[32];
|
|
|
|
|
|
|
|
switch (ut) {
|
|
|
|
case(URI_TYPE_REQUEST):
|
|
|
|
context = PJSIP_URI_IN_REQ_URI;
|
|
|
|
strcpy(header_name, "Request"); /* Safe */
|
|
|
|
local_uri = rdata->msg_info.msg->line.req.uri;
|
|
|
|
break;
|
|
|
|
case(PJSIP_H_FROM):
|
|
|
|
strcpy(header_name, "From"); /* Safe */
|
|
|
|
context = PJSIP_URI_IN_FROMTO_HDR;
|
|
|
|
local_uri = pjsip_uri_get_uri(((pjsip_from_hdr *)hdr)->uri);
|
|
|
|
break;
|
|
|
|
case(PJSIP_H_TO):
|
|
|
|
strcpy(header_name, "To"); /* Safe */
|
|
|
|
context = PJSIP_URI_IN_FROMTO_HDR;
|
|
|
|
local_uri = pjsip_uri_get_uri(((pjsip_to_hdr *)hdr)->uri);
|
|
|
|
break;
|
|
|
|
case(PJSIP_H_CONTACT):
|
|
|
|
strcpy(header_name, "Contact"); /* Safe */
|
|
|
|
context = PJSIP_URI_IN_CONTACT_HDR;
|
|
|
|
local_uri = pjsip_uri_get_uri(((pjsip_contact_hdr *)hdr)->uri);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdrbuf_len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, rdata->msg_info.msg->line.req.uri, hdrbuf, 512);
|
|
|
|
hdrbuf[hdrbuf_len] = '\0';
|
|
|
|
request_uri = ast_strdupa(hdrbuf);
|
|
|
|
hdrbuf_len = pjsip_uri_print(context, local_uri, hdrbuf, 512);
|
|
|
|
hdrbuf[hdrbuf_len] = '\0';
|
|
|
|
|
|
|
|
ast_debug(2, "There was a non sip(s) URI scheme in %s URI '%s' for request '%*.*s %s'\n",
|
|
|
|
header_name, hdrbuf,
|
|
|
|
(int)rdata->msg_info.msg->line.req.method.name.slen,
|
|
|
|
(int)rdata->msg_info.msg->line.req.method.name.slen,
|
|
|
|
rdata->msg_info.msg->line.req.method.name.ptr, request_uri);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-08-26 02:20:13 +00:00
|
|
|
/*!
|
|
|
|
* /internal
|
|
|
|
*
|
|
|
|
* We want to make sure that any incoming requests don't already
|
|
|
|
* have x-ast-* parameters in any URIs or we may get confused
|
|
|
|
* if symmetric transport (x-ast-txp) or rewrite_contact (x-ast-orig-host)
|
|
|
|
* is used later on.
|
|
|
|
*/
|
|
|
|
static void remove_x_ast_params(pjsip_uri *header_uri){
|
|
|
|
pjsip_sip_uri *uri;
|
|
|
|
pjsip_param *param;
|
|
|
|
|
|
|
|
if (!header_uri) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uri = pjsip_uri_get_uri(header_uri);
|
|
|
|
if (!uri) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
param = uri->other_param.next;
|
|
|
|
|
|
|
|
while (param != &uri->other_param) {
|
|
|
|
/* We need to save off 'next' because pj_list_erase will remove it */
|
|
|
|
pjsip_param *next = param->next;
|
|
|
|
|
|
|
|
if (pj_strncmp2(¶m->name, "x-ast-", 6) == 0) {
|
|
|
|
pj_list_erase(param);
|
|
|
|
}
|
|
|
|
param = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-13 21:23:54 +00:00
|
|
|
static pj_bool_t on_rx_process_uris(pjsip_rx_data *rdata)
|
|
|
|
{
|
|
|
|
pjsip_contact_hdr *contact = NULL;
|
|
|
|
|
|
|
|
if (rdata->msg_info.msg->type != PJSIP_REQUEST_MSG) {
|
|
|
|
return PJ_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!is_sip_uri(rdata->msg_info.msg->line.req.uri)) {
|
|
|
|
print_uri_debug(URI_TYPE_REQUEST, rdata, NULL);
|
|
|
|
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata,
|
|
|
|
PJSIP_SC_UNSUPPORTED_URI_SCHEME, NULL, NULL, NULL);
|
|
|
|
return PJ_TRUE;
|
|
|
|
}
|
2019-08-26 02:20:13 +00:00
|
|
|
remove_x_ast_params(rdata->msg_info.msg->line.req.uri);
|
2017-09-13 21:23:54 +00:00
|
|
|
|
|
|
|
if (!is_sip_uri(rdata->msg_info.from->uri)) {
|
|
|
|
print_uri_debug(URI_TYPE_FROM, rdata, (pjsip_hdr *)rdata->msg_info.from);
|
|
|
|
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata,
|
|
|
|
PJSIP_SC_UNSUPPORTED_URI_SCHEME, NULL, NULL, NULL);
|
|
|
|
return PJ_TRUE;
|
|
|
|
}
|
2019-08-26 02:20:13 +00:00
|
|
|
remove_x_ast_params(rdata->msg_info.from->uri);
|
2017-09-13 21:23:54 +00:00
|
|
|
|
|
|
|
if (!is_sip_uri(rdata->msg_info.to->uri)) {
|
|
|
|
print_uri_debug(URI_TYPE_TO, rdata, (pjsip_hdr *)rdata->msg_info.to);
|
|
|
|
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata,
|
|
|
|
PJSIP_SC_UNSUPPORTED_URI_SCHEME, NULL, NULL, NULL);
|
|
|
|
return PJ_TRUE;
|
|
|
|
}
|
2019-08-26 02:20:13 +00:00
|
|
|
remove_x_ast_params(rdata->msg_info.to->uri);
|
2017-12-20 22:17:40 +00:00
|
|
|
|
|
|
|
contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(
|
|
|
|
rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
|
|
|
|
|
|
|
|
if (!contact && pjsip_method_creates_dialog(&rdata->msg_info.msg->line.req.method)) {
|
|
|
|
/* A contact header is required for dialog creating methods */
|
|
|
|
static const pj_str_t missing_contact = { "Missing Contact header", 22 };
|
|
|
|
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400,
|
|
|
|
&missing_contact, NULL, NULL);
|
|
|
|
return PJ_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (contact) {
|
2017-09-13 21:23:54 +00:00
|
|
|
if (!contact->star && !is_sip_uri(contact->uri)) {
|
|
|
|
print_uri_debug(URI_TYPE_CONTACT, rdata, (pjsip_hdr *)contact);
|
|
|
|
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata,
|
|
|
|
PJSIP_SC_UNSUPPORTED_URI_SCHEME, NULL, NULL, NULL);
|
|
|
|
return PJ_TRUE;
|
|
|
|
}
|
2019-08-26 02:20:13 +00:00
|
|
|
remove_x_ast_params(contact->uri);
|
|
|
|
|
2017-12-20 22:17:40 +00:00
|
|
|
contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(
|
|
|
|
rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next);
|
2017-09-13 21:23:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return PJ_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static pj_bool_t on_rx_process_symmetric_transport(pjsip_rx_data *rdata)
|
res_pjsip: Symmetric transports
A new transport parameter 'symmetric_transport' has been added.
When a request from a dynamic contact comes in on a transport with
this option set to 'yes', the transport name will be saved and used
for subsequent outgoing requests like OPTIONS, NOTIFY and INVITE.
It's saved as a contact uri parameter named 'x-ast-txp' and will
display with the contact uri in CLI, AMI, and ARI output. On the
outgoing request, if a transport wasn't explicitly set on the
endpoint AND the request URI is not a hostname, the saved transport
will be used and the 'x-ast-txp' parameter stripped from the
outgoing packet.
* config_transport was modified to accept and store the new parameter.
* config_transport/transport_apply was updated to store the transport
name in the pjsip_transport->info field using the pjsip_transport->pool
on UDP transports.
* A 'multihomed_on_rx_message' function was added to
pjsip_message_ip_updater that, for incoming requests, retrieves the
transport name from pjsip_transport->info and retrieves the transport.
If transport->symmetric_transport is set, an 'x-ast-txp' uri parameter
containing the transport name is added to the incoming Contact header.
* An 'ast_sip_get_transport_name' function was added to res_pjsip.
It takes an ast_sip_endpoint and a pjsip_sip_uri and returns a
transport name if endpoint->transport is set or if there's an
'x-ast-txp' parameter on the uri and the uri host is an ipv4 or
ipv6 address. Otherwise it returns NULL.
* An 'ast_sip_dlg_set_transport' function was added to res_pjsip
which takes an ast_sip_endpoint, a pjsip_dialog, and an optional
pjsip_tpselector. It calls ast_sip_get_transport_name() and if
a non-NULL is returned, sets the selector and sets the transport
on the dialog. If a selector was passed in, it's updated.
* res_pjsip/ast_sip_create_dialog_uac and ast_sip_create_dialog_uas
were modified to call ast_sip_dlg_set_transport() instead of their
original logic.
* res_pjsip/create_out_of_dialog_request was modified to call
ast_sip_get_transport_name() and pjsip_tx_data_set_transport()
instead of its original logic.
* Existing transport logic was removed from endpt_send_request
since that can only be called after a create_out_of_dialog_request.
* res_pjsip/ast_sip_create_rdata was converted to a wrapper around
a new 'ast_sip_create_rdata_with_contact' function which allows
a contact_uri to be specified in addition to the existing
parameters. (See below)
* res_pjsip_pubsub/internal_pjsip_evsub_send_request was eliminated
since all it did was transport selection and that is now done in
ast_sip_create_dialog_uac and ast_sip_create_dialog_uas.
* 'contact_uri' was added to subscription_persistence. This was
necessary because although the parsed rdata contact header has the
x-ast-txp parameter added (if appropriate),
subscription_persistence_update stores the raw packet which
doesn't have it. subscription_persistence_recreate was then
updated to call ast_sip_create_rdata_with_contact with the
persisted contact_uri so the recreated subscription has the
correct transport info to send the NOTIFYs.
* res_pjsip_session/internal_pjsip_inv_send_msg was eliminated since
all it did was transport selection and that is now done in
ast_sip_create_dialog_uac.
* pjsip_message_ip_updater/multihomed_on_tx_message was updated
to remove all traces of the x-ast-txp parameter from the
outgoing headers.
NOTE: This change does NOT modify the behavior of permanent
contacts specified on an aor. To do so would require that the
permanent contact's contact uri be updated with the x-ast-txp
parameter and the aor sorcery object updated. If we need to
persue this, we need to think about cloning permanent contacts into
the same store as the dynamic ones on an aor load so they can be
updated without disturbing the originally configured value.
You CAN add the x-ast-txp parameter to a permanent contact's uri
but it would be much simpler to just set endpoint->transport.
Change-Id: I4ee1f51473da32ca54b877cd158523efcef9655f
2017-03-07 14:33:26 +00:00
|
|
|
{
|
|
|
|
pjsip_contact_hdr *contact;
|
|
|
|
pjsip_sip_uri *uri;
|
|
|
|
const char *transport_id;
|
|
|
|
struct ast_sip_transport *transport;
|
|
|
|
pjsip_param *x_transport;
|
|
|
|
|
|
|
|
if (rdata->msg_info.msg->type != PJSIP_REQUEST_MSG) {
|
|
|
|
return PJ_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
|
|
|
|
if (!(contact && contact->uri
|
|
|
|
&& ast_begins_with(rdata->tp_info.transport->info, AST_SIP_X_AST_TXP ":"))) {
|
|
|
|
return PJ_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
uri = pjsip_uri_get_uri(contact->uri);
|
|
|
|
|
|
|
|
transport_id = rdata->tp_info.transport->info + AST_SIP_X_AST_TXP_LEN + 1;
|
|
|
|
transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", transport_id);
|
|
|
|
|
|
|
|
if (!(transport && transport->symmetric_transport)) {
|
2017-04-03 18:56:43 +00:00
|
|
|
ao2_cleanup(transport);
|
res_pjsip: Symmetric transports
A new transport parameter 'symmetric_transport' has been added.
When a request from a dynamic contact comes in on a transport with
this option set to 'yes', the transport name will be saved and used
for subsequent outgoing requests like OPTIONS, NOTIFY and INVITE.
It's saved as a contact uri parameter named 'x-ast-txp' and will
display with the contact uri in CLI, AMI, and ARI output. On the
outgoing request, if a transport wasn't explicitly set on the
endpoint AND the request URI is not a hostname, the saved transport
will be used and the 'x-ast-txp' parameter stripped from the
outgoing packet.
* config_transport was modified to accept and store the new parameter.
* config_transport/transport_apply was updated to store the transport
name in the pjsip_transport->info field using the pjsip_transport->pool
on UDP transports.
* A 'multihomed_on_rx_message' function was added to
pjsip_message_ip_updater that, for incoming requests, retrieves the
transport name from pjsip_transport->info and retrieves the transport.
If transport->symmetric_transport is set, an 'x-ast-txp' uri parameter
containing the transport name is added to the incoming Contact header.
* An 'ast_sip_get_transport_name' function was added to res_pjsip.
It takes an ast_sip_endpoint and a pjsip_sip_uri and returns a
transport name if endpoint->transport is set or if there's an
'x-ast-txp' parameter on the uri and the uri host is an ipv4 or
ipv6 address. Otherwise it returns NULL.
* An 'ast_sip_dlg_set_transport' function was added to res_pjsip
which takes an ast_sip_endpoint, a pjsip_dialog, and an optional
pjsip_tpselector. It calls ast_sip_get_transport_name() and if
a non-NULL is returned, sets the selector and sets the transport
on the dialog. If a selector was passed in, it's updated.
* res_pjsip/ast_sip_create_dialog_uac and ast_sip_create_dialog_uas
were modified to call ast_sip_dlg_set_transport() instead of their
original logic.
* res_pjsip/create_out_of_dialog_request was modified to call
ast_sip_get_transport_name() and pjsip_tx_data_set_transport()
instead of its original logic.
* Existing transport logic was removed from endpt_send_request
since that can only be called after a create_out_of_dialog_request.
* res_pjsip/ast_sip_create_rdata was converted to a wrapper around
a new 'ast_sip_create_rdata_with_contact' function which allows
a contact_uri to be specified in addition to the existing
parameters. (See below)
* res_pjsip_pubsub/internal_pjsip_evsub_send_request was eliminated
since all it did was transport selection and that is now done in
ast_sip_create_dialog_uac and ast_sip_create_dialog_uas.
* 'contact_uri' was added to subscription_persistence. This was
necessary because although the parsed rdata contact header has the
x-ast-txp parameter added (if appropriate),
subscription_persistence_update stores the raw packet which
doesn't have it. subscription_persistence_recreate was then
updated to call ast_sip_create_rdata_with_contact with the
persisted contact_uri so the recreated subscription has the
correct transport info to send the NOTIFYs.
* res_pjsip_session/internal_pjsip_inv_send_msg was eliminated since
all it did was transport selection and that is now done in
ast_sip_create_dialog_uac.
* pjsip_message_ip_updater/multihomed_on_tx_message was updated
to remove all traces of the x-ast-txp parameter from the
outgoing headers.
NOTE: This change does NOT modify the behavior of permanent
contacts specified on an aor. To do so would require that the
permanent contact's contact uri be updated with the x-ast-txp
parameter and the aor sorcery object updated. If we need to
persue this, we need to think about cloning permanent contacts into
the same store as the dynamic ones on an aor load so they can be
updated without disturbing the originally configured value.
You CAN add the x-ast-txp parameter to a permanent contact's uri
but it would be much simpler to just set endpoint->transport.
Change-Id: I4ee1f51473da32ca54b877cd158523efcef9655f
2017-03-07 14:33:26 +00:00
|
|
|
return PJ_FALSE;
|
|
|
|
}
|
2017-04-03 18:56:43 +00:00
|
|
|
ao2_cleanup(transport);
|
res_pjsip: Symmetric transports
A new transport parameter 'symmetric_transport' has been added.
When a request from a dynamic contact comes in on a transport with
this option set to 'yes', the transport name will be saved and used
for subsequent outgoing requests like OPTIONS, NOTIFY and INVITE.
It's saved as a contact uri parameter named 'x-ast-txp' and will
display with the contact uri in CLI, AMI, and ARI output. On the
outgoing request, if a transport wasn't explicitly set on the
endpoint AND the request URI is not a hostname, the saved transport
will be used and the 'x-ast-txp' parameter stripped from the
outgoing packet.
* config_transport was modified to accept and store the new parameter.
* config_transport/transport_apply was updated to store the transport
name in the pjsip_transport->info field using the pjsip_transport->pool
on UDP transports.
* A 'multihomed_on_rx_message' function was added to
pjsip_message_ip_updater that, for incoming requests, retrieves the
transport name from pjsip_transport->info and retrieves the transport.
If transport->symmetric_transport is set, an 'x-ast-txp' uri parameter
containing the transport name is added to the incoming Contact header.
* An 'ast_sip_get_transport_name' function was added to res_pjsip.
It takes an ast_sip_endpoint and a pjsip_sip_uri and returns a
transport name if endpoint->transport is set or if there's an
'x-ast-txp' parameter on the uri and the uri host is an ipv4 or
ipv6 address. Otherwise it returns NULL.
* An 'ast_sip_dlg_set_transport' function was added to res_pjsip
which takes an ast_sip_endpoint, a pjsip_dialog, and an optional
pjsip_tpselector. It calls ast_sip_get_transport_name() and if
a non-NULL is returned, sets the selector and sets the transport
on the dialog. If a selector was passed in, it's updated.
* res_pjsip/ast_sip_create_dialog_uac and ast_sip_create_dialog_uas
were modified to call ast_sip_dlg_set_transport() instead of their
original logic.
* res_pjsip/create_out_of_dialog_request was modified to call
ast_sip_get_transport_name() and pjsip_tx_data_set_transport()
instead of its original logic.
* Existing transport logic was removed from endpt_send_request
since that can only be called after a create_out_of_dialog_request.
* res_pjsip/ast_sip_create_rdata was converted to a wrapper around
a new 'ast_sip_create_rdata_with_contact' function which allows
a contact_uri to be specified in addition to the existing
parameters. (See below)
* res_pjsip_pubsub/internal_pjsip_evsub_send_request was eliminated
since all it did was transport selection and that is now done in
ast_sip_create_dialog_uac and ast_sip_create_dialog_uas.
* 'contact_uri' was added to subscription_persistence. This was
necessary because although the parsed rdata contact header has the
x-ast-txp parameter added (if appropriate),
subscription_persistence_update stores the raw packet which
doesn't have it. subscription_persistence_recreate was then
updated to call ast_sip_create_rdata_with_contact with the
persisted contact_uri so the recreated subscription has the
correct transport info to send the NOTIFYs.
* res_pjsip_session/internal_pjsip_inv_send_msg was eliminated since
all it did was transport selection and that is now done in
ast_sip_create_dialog_uac.
* pjsip_message_ip_updater/multihomed_on_tx_message was updated
to remove all traces of the x-ast-txp parameter from the
outgoing headers.
NOTE: This change does NOT modify the behavior of permanent
contacts specified on an aor. To do so would require that the
permanent contact's contact uri be updated with the x-ast-txp
parameter and the aor sorcery object updated. If we need to
persue this, we need to think about cloning permanent contacts into
the same store as the dynamic ones on an aor load so they can be
updated without disturbing the originally configured value.
You CAN add the x-ast-txp parameter to a permanent contact's uri
but it would be much simpler to just set endpoint->transport.
Change-Id: I4ee1f51473da32ca54b877cd158523efcef9655f
2017-03-07 14:33:26 +00:00
|
|
|
|
|
|
|
x_transport = PJ_POOL_ALLOC_T(rdata->tp_info.pool, pjsip_param);
|
|
|
|
x_transport->name = pj_strdup3(rdata->tp_info.pool, AST_SIP_X_AST_TXP);
|
|
|
|
x_transport->value = pj_strdup3(rdata->tp_info.pool, transport_id);
|
|
|
|
|
|
|
|
pj_list_insert_before(&uri->other_param, x_transport);
|
|
|
|
|
|
|
|
ast_debug(1, "Set transport '%s' on %.*s from %.*s:%d\n", transport_id,
|
|
|
|
(int)rdata->msg_info.msg->line.req.method.name.slen,
|
|
|
|
rdata->msg_info.msg->line.req.method.name.ptr,
|
|
|
|
(int)uri->host.slen, uri->host.ptr, uri->port);
|
|
|
|
|
|
|
|
return PJ_FALSE;
|
|
|
|
}
|
|
|
|
|
2017-09-13 21:23:54 +00:00
|
|
|
static pj_bool_t filter_on_rx_message(pjsip_rx_data *rdata)
|
|
|
|
{
|
|
|
|
pj_bool_t rc;
|
|
|
|
|
|
|
|
rc = on_rx_process_uris(rdata);
|
|
|
|
if (rc == PJ_TRUE) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = on_rx_process_symmetric_transport(rdata);
|
|
|
|
if (rc == PJ_TRUE) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PJ_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ast_res_pjsip_cleanup_message_filter(void)
|
2014-03-11 16:07:42 +00:00
|
|
|
{
|
2017-12-29 08:57:17 +00:00
|
|
|
ast_sip_unregister_service(&filter_module_tsx);
|
|
|
|
ast_sip_unregister_service(&filter_module_transport);
|
|
|
|
ast_sip_unregister_supplement(&filter_supplement);
|
|
|
|
ast_sip_session_unregister_supplement(&filter_session_supplement);
|
2014-03-11 16:07:42 +00:00
|
|
|
}
|
|
|
|
|
2017-09-13 21:23:54 +00:00
|
|
|
int ast_res_pjsip_init_message_filter(void)
|
2014-03-11 16:07:42 +00:00
|
|
|
{
|
2017-12-29 08:57:17 +00:00
|
|
|
ast_sip_session_register_supplement(&filter_session_supplement);
|
|
|
|
ast_sip_register_supplement(&filter_supplement);
|
2014-11-05 12:19:09 +00:00
|
|
|
|
2017-12-29 08:57:17 +00:00
|
|
|
if (ast_sip_register_service(&filter_module_transport)) {
|
2017-09-26 16:01:48 +00:00
|
|
|
ast_log(LOG_ERROR, "Could not register message filter module for incoming and outgoing requests\n");
|
|
|
|
ast_res_pjsip_cleanup_message_filter();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-12-29 08:57:17 +00:00
|
|
|
if (ast_sip_register_service(&filter_module_tsx)) {
|
2017-09-13 21:23:54 +00:00
|
|
|
ast_log(LOG_ERROR, "Could not register message filter module for incoming and outgoing requests\n");
|
|
|
|
ast_res_pjsip_cleanup_message_filter();
|
2016-09-19 11:13:21 +00:00
|
|
|
return -1;
|
2014-03-11 16:07:42 +00:00
|
|
|
}
|
|
|
|
|
2016-09-19 11:13:21 +00:00
|
|
|
return 0;
|
2014-03-11 16:07:42 +00:00
|
|
|
}
|