summaryrefslogtreecommitdiff
path: root/res
diff options
context:
space:
mode:
Diffstat (limited to 'res')
-rw-r--r--res/res_pjsip.c173
-rw-r--r--res/res_pjsip/config_transport.c22
-rw-r--r--res/res_pjsip/pjsip_configuration.c1
-rw-r--r--res/res_pjsip/pjsip_message_ip_updater.c83
-rw-r--r--res/res_pjsip_endpoint_identifier_ip.c101
-rw-r--r--res/res_pjsip_outbound_registration.c18
-rw-r--r--res/res_pjsip_pubsub.c46
-rw-r--r--res/res_pjsip_refer.c7
-rw-r--r--res/res_pjsip_sdp_rtp.c68
-rw-r--r--res/res_pjsip_session.c34
-rw-r--r--res/res_pjsip_transport_websocket.c30
-rw-r--r--res/res_rtp_asterisk.c367
12 files changed, 688 insertions, 262 deletions
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 54a0a5f39..7b10f47f6 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -944,6 +944,16 @@
to the receiving one.
</para></description>
</configOption>
+ <configOption name="rtcp_mux" default="no">
+ <synopsis>Enable RFC 5761 RTCP multiplexing on the RTP port</synopsis>
+ <description><para>
+ With this option enabled, Asterisk will attempt to negotiate the use of the "rtcp-mux"
+ attribute on all media streams. This will result in RTP and RTCP being sent and received
+ on the same port. This shifts the demultiplexing logic to the application rather than
+ the transport layer. This option is useful when interoperating with WebRTC endpoints
+ since they mandate this option's use.
+ </para></description>
+ </configOption>
</configObject>
<configObject name="auth">
<synopsis>Authentication type</synopsis>
@@ -1177,6 +1187,22 @@
in-progress calls.</para>
</description>
</configOption>
+ <configOption name="symmetric_transport" default="no">
+ <synopsis>Use the same transport for outgoing reqests as incoming ones.</synopsis>
+ <description>
+ <para>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.
+ </para>
+ </description>
+ </configOption>
</configObject>
<configObject name="contact">
<synopsis>A way of creating an aliased name to a SIP URI</synopsis>
@@ -2750,12 +2776,59 @@ 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 char *domain, const pj_str_t *target, pjsip_tpselector *selector)
+int ast_sip_get_transport_name(const struct ast_sip_endpoint *endpoint,
+ pjsip_sip_uri *sip_uri, char *buf, size_t buf_len)
+{
+ char *host = NULL;
+ static const pj_str_t x_name = { AST_SIP_X_AST_TXP, AST_SIP_X_AST_TXP_LEN };
+ pjsip_param *x_transport;
+
+ if (!ast_strlen_zero(endpoint->transport)) {
+ ast_copy_string(buf, endpoint->transport, buf_len);
+ return 0;
+ }
+
+ x_transport = pjsip_param_find(&sip_uri->other_param, &x_name);
+ if (!x_transport) {
+ return -1;
+ }
+
+ /* Only use x_transport if the uri host is an ip (4 or 6) address */
+ host = ast_alloca(sip_uri->host.slen + 1);
+ ast_copy_pj_str(host, &sip_uri->host, sip_uri->host.slen + 1);
+ if (!ast_sockaddr_parse(NULL, host, PARSE_PORT_FORBID)) {
+ return -1;
+ }
+
+ ast_copy_pj_str(buf, &x_transport->value, buf_len);
+
+ return 0;
+}
+
+int ast_sip_dlg_set_transport(const struct ast_sip_endpoint *endpoint, pjsip_dialog *dlg,
+ pjsip_tpselector *selector)
+{
+ pjsip_sip_uri *uri;
+ pjsip_tpselector sel = { .type = PJSIP_TPSELECTOR_NONE, };
+
+ uri = pjsip_uri_get_uri(dlg->target);
+ if (!selector) {
+ selector = &sel;
+ }
+
+ ast_sip_set_tpselector_from_ep_or_uri(endpoint, uri, selector);
+ pjsip_dlg_set_transport(dlg, selector);
+
+ return 0;
+}
+
+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;
pjsip_sip_uri *sip_uri;
- pjsip_transport_type_e type = PJSIP_TRANSPORT_UNSPECIFIED;
+ pjsip_transport_type_e type;
int local_port;
char default_user[PJSIP_MAX_URL_SIZE];
@@ -2775,21 +2848,21 @@ static int sip_dialog_create_from(pj_pool_t *pool, pj_str_t *from, const char *u
sip_uri = pjsip_uri_get_uri(uri);
/* Determine the transport type to use */
+ type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri)) {
- type = PJSIP_TRANSPORT_TLS;
+ if (type == PJSIP_TRANSPORT_UNSPECIFIED
+ || !(pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE)) {
+ type = PJSIP_TRANSPORT_TLS;
+ }
} else if (!sip_uri->transport_param.slen) {
type = PJSIP_TRANSPORT_UDP;
- } else {
- type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
- }
-
- if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
+ } else if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
return -1;
}
/* If the host is IPv6 turn the transport into an IPv6 version */
- if (pj_strchr(&sip_uri->host, ':') && type < PJSIP_TRANSPORT_START_OTHER) {
- type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
+ if (pj_strchr(&sip_uri->host, ':')) {
+ type |= PJSIP_TRANSPORT_IPV6;
}
if (!ast_strlen_zero(domain)) {
@@ -2813,8 +2886,8 @@ static int sip_dialog_create_from(pj_pool_t *pool, pj_str_t *from, const char *u
}
/* If IPv6 was specified in the transport, set the proper type */
- if (pj_strchr(&local_addr, ':') && type < PJSIP_TRANSPORT_START_OTHER) {
- type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
+ if (pj_strchr(&local_addr, ':')) {
+ type |= PJSIP_TRANSPORT_IPV6;
}
from->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
@@ -2880,15 +2953,16 @@ int ast_sip_set_tpselector_from_transport_name(const char *transport_name, pjsip
return ast_sip_set_tpselector_from_transport(transport, selector);
}
-static int sip_get_tpselector_from_endpoint(const struct ast_sip_endpoint *endpoint, pjsip_tpselector *selector)
+int ast_sip_set_tpselector_from_ep_or_uri(const struct ast_sip_endpoint *endpoint,
+ pjsip_sip_uri *sip_uri, pjsip_tpselector *selector)
{
- const char *transport_name = endpoint->transport;
+ char transport_name[128];
- if (ast_strlen_zero(transport_name)) {
+ if (ast_sip_get_transport_name(endpoint, sip_uri, transport_name, sizeof(transport_name))) {
return 0;
}
- return ast_sip_set_tpselector_from_transport_name(endpoint->transport, selector);
+ return ast_sip_set_tpselector_from_transport_name(transport_name, selector);
}
void ast_sip_add_usereqphone(const struct ast_sip_endpoint *endpoint, pj_pool_t *pool, pjsip_uri *uri)
@@ -2896,8 +2970,8 @@ void ast_sip_add_usereqphone(const struct ast_sip_endpoint *endpoint, pj_pool_t
pjsip_sip_uri *sip_uri;
int i = 0;
pjsip_param *param;
- const pj_str_t STR_USER = { "user", 4 };
- const pj_str_t STR_PHONE = { "phone", 5 };
+ static const pj_str_t STR_USER = { "user", 4 };
+ static const pj_str_t STR_PHONE = { "phone", 5 };
if (!endpoint || !endpoint->usereqphone || (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))) {
return;
@@ -2930,7 +3004,8 @@ void ast_sip_add_usereqphone(const struct ast_sip_endpoint *endpoint, pj_pool_t
pj_list_insert_before(&sip_uri->other_param, param);
}
-pjsip_dialog *ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint, const char *uri, const char *request_user)
+pjsip_dialog *ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint,
+ const char *uri, const char *request_user)
{
char enclosed_uri[PJSIP_MAX_URL_SIZE];
pj_str_t local_uri = { "sip:temp@temp", 13 }, remote_uri, target_uri;
@@ -2955,12 +3030,13 @@ pjsip_dialog *ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint,
return NULL;
}
- if (sip_get_tpselector_from_endpoint(endpoint, &selector)) {
- pjsip_dlg_terminate(dlg);
- return NULL;
- }
+ /* We have to temporarily bump up the sess_count here so the dialog is not prematurely destroyed */
+ dlg->sess_count++;
+
+ ast_sip_dlg_set_transport(endpoint, dlg, &selector);
if (sip_dialog_create_from(dlg->pool, &local_uri, endpoint->fromuser, endpoint->fromdomain, &remote_uri, &selector)) {
+ dlg->sess_count--;
pjsip_dlg_terminate(dlg);
return NULL;
}
@@ -2996,11 +3072,6 @@ pjsip_dialog *ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint,
ast_sip_add_usereqphone(endpoint, dlg->pool, dlg->target);
ast_sip_add_usereqphone(endpoint, dlg->pool, dlg->remote.info->uri);
- /* We have to temporarily bump up the sess_count here so the dialog is not prematurely destroyed */
- dlg->sess_count++;
-
- pjsip_dlg_set_transport(dlg, &selector);
-
if (!ast_strlen_zero(outbound_proxy)) {
pjsip_route_hdr route_set, *route;
static const pj_str_t ROUTE_HNAME = { "Route", 5 };
@@ -3069,10 +3140,13 @@ pjsip_dialog *ast_sip_create_dialog_uas(const struct ast_sip_endpoint *endpoint,
pjsip_transport_type_e type = rdata->tp_info.transport->key.type;
pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
pjsip_transport *transport;
+ pjsip_contact_hdr *contact_hdr;
ast_assert(status != NULL);
- if (sip_get_tpselector_from_endpoint(endpoint, &selector)) {
+ contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
+ if (ast_sip_set_tpselector_from_ep_or_uri(endpoint, pjsip_uri_get_uri(contact_hdr->uri),
+ &selector)) {
return NULL;
}
@@ -3118,8 +3192,8 @@ pjsip_dialog *ast_sip_create_dialog_uas(const struct ast_sip_endpoint *endpoint,
return dlg;
}
-int ast_sip_create_rdata(pjsip_rx_data *rdata, char *packet, const char *src_name, int src_port,
- char *transport_type, const char *local_name, int local_port)
+int ast_sip_create_rdata_with_contact(pjsip_rx_data *rdata, char *packet, const char *src_name, int src_port,
+ char *transport_type, const char *local_name, int local_port, const char *contact)
{
pj_str_t tmp;
@@ -3143,6 +3217,16 @@ int ast_sip_create_rdata(pjsip_rx_data *rdata, char *packet, const char *src_nam
return -1;
}
+ if (!ast_strlen_zero(contact)) {
+ pjsip_contact_hdr *contact_hdr;
+
+ contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
+ if (contact_hdr) {
+ contact_hdr->uri = pjsip_parse_uri(rdata->tp_info.pool, (char *)contact,
+ strlen(contact), PJSIP_PARSE_URI_AS_NAMEADDR);
+ }
+ }
+
pj_strdup2(rdata->tp_info.pool, &rdata->msg_info.via->recvd_param, rdata->pkt_info.src_name);
rdata->msg_info.via->rport_param = -1;
@@ -3154,6 +3238,13 @@ int ast_sip_create_rdata(pjsip_rx_data *rdata, char *packet, const char *src_nam
return 0;
}
+int ast_sip_create_rdata(pjsip_rx_data *rdata, char *packet, const char *src_name, int src_port,
+ char *transport_type, const char *local_name, int local_port)
+{
+ return ast_sip_create_rdata_with_contact(rdata, packet, src_name, src_port, transport_type,
+ local_name, local_port, NULL);
+}
+
/* PJSIP doesn't know about the INFO method, so we have to define it ourselves */
static const pjsip_method info_method = {PJSIP_OTHER_METHOD, {"INFO", 4} };
static const pjsip_method message_method = {PJSIP_OTHER_METHOD, {"MESSAGE", 7} };
@@ -3235,14 +3326,6 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s
pj_cstr(&remote_uri, uri);
}
- if (endpoint) {
- if (sip_get_tpselector_from_endpoint(endpoint, &selector)) {
- ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport selector for endpoint %s\n",
- ast_sorcery_object_get_id(endpoint));
- return -1;
- }
- }
-
pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Outbound request", 256, 256);
if (!pool) {
@@ -3260,6 +3343,8 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s
return -1;
}
+ ast_sip_set_tpselector_from_ep_or_uri(endpoint, pjsip_uri_get_uri(sip_uri), &selector);
+
fromuser = endpoint ? (!ast_strlen_zero(endpoint->fromuser) ? endpoint->fromuser : ast_sorcery_object_get_id(endpoint)) : NULL;
if (sip_dialog_create_from(pool, &from, fromuser,
endpoint ? endpoint->fromdomain : NULL, &remote_uri, &selector)) {
@@ -3279,6 +3364,8 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s
return -1;
}
+ pjsip_tx_data_set_transport(*tdata, &selector);
+
if (endpoint && !ast_strlen_zero(endpoint->contact_user)){
pjsip_contact_hdr *contact_hdr;
pjsip_sip_uri *contact_uri;
@@ -3320,6 +3407,8 @@ int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg,
{
const pjsip_method *pmethod = get_pjsip_method(method);
+ ast_assert(endpoint != NULL);
+
if (!pmethod) {
ast_log(LOG_WARNING, "Unknown method '%s'. Cannot send request\n", method);
return -1;
@@ -3584,7 +3673,6 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
struct send_request_wrapper *req_wrapper;
pj_status_t ret_val;
pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
- pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
if (!cb && token) {
/* Silly. Without a callback we cannot do anything with token. */
@@ -3609,11 +3697,6 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
/* Add a reference to tdata. The wrapper destructor cleans it up. */
pjsip_tx_data_add_ref(tdata);
- if (endpoint) {
- sip_get_tpselector_from_endpoint(endpoint, &selector);
- pjsip_tx_data_set_transport(tdata, &selector);
- }
-
if (timeout > 0) {
pj_time_val timeout_timer_val = { timeout / 1000, timeout % 1000 };
diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c
index 60b4507cd..3c41f175a 100644
--- a/res/res_pjsip/config_transport.c
+++ b/res/res_pjsip/config_transport.c
@@ -552,13 +552,20 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
}
}
- if (res == PJ_SUCCESS && (transport->tos || transport->cos)) {
- pj_sock_t sock;
- pj_qos_params qos_params;
- sock = pjsip_udp_transport_get_socket(temp_state->state->transport);
- pj_sock_get_qos_params(sock, &qos_params);
- set_qos(transport, &qos_params);
- pj_sock_set_qos_params(sock, &qos_params);
+ if (res == PJ_SUCCESS) {
+ temp_state->state->transport->info = pj_pool_alloc(temp_state->state->transport->pool,
+ (AST_SIP_X_AST_TXP_LEN + strlen(transport_id) + 2));
+
+ sprintf(temp_state->state->transport->info, "%s:%s", AST_SIP_X_AST_TXP, transport_id);
+
+ if (transport->tos || transport->cos) {
+ pj_sock_t sock;
+ pj_qos_params qos_params;
+ sock = pjsip_udp_transport_get_socket(temp_state->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;
@@ -1375,6 +1382,7 @@ int ast_sip_initialize_sorcery_transport(void)
ast_sorcery_object_field_register(sorcery, "transport", "cos", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, cos));
ast_sorcery_object_field_register(sorcery, "transport", "websocket_write_timeout", AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT_STR, OPT_INT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_transport, write_timeout), 1, INT_MAX);
ast_sorcery_object_field_register(sorcery, "transport", "allow_reload", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_transport, allow_reload));
+ ast_sorcery_object_field_register(sorcery, "transport", "symmetric_transport", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_transport, symmetric_transport));
internal_sip_register_endpoint_formatter(&endpoint_transport_formatter);
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index bfaf750d4..eb8e19712 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -1938,6 +1938,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
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);
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "asymmetric_rtp_codec", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, asymmetric_rtp_codec));
+ ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtcp_mux", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, rtcp_mux));
if (ast_sip_initialize_sorcery_transport()) {
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
diff --git a/res/res_pjsip/pjsip_message_ip_updater.c b/res/res_pjsip/pjsip_message_ip_updater.c
index 7671ad0a7..864d898b3 100644
--- a/res/res_pjsip/pjsip_message_ip_updater.c
+++ b/res/res_pjsip/pjsip_message_ip_updater.c
@@ -28,6 +28,7 @@
#define MOD_DATA_RESTRICTIONS "restrictions"
static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata);
+static pj_bool_t multihomed_on_rx_message(pjsip_rx_data *rdata);
/*! \brief Outgoing message modification restrictions */
struct multihomed_message_restrictions {
@@ -41,6 +42,7 @@ static pjsip_module multihomed_module = {
.priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 1,
.on_tx_request = multihomed_on_tx_message,
.on_tx_response = multihomed_on_tx_message,
+ .on_rx_request = multihomed_on_rx_message,
};
/*! \brief Helper function to get (or allocate if not already present) restrictions on a message */
@@ -151,6 +153,44 @@ static int multihomed_rewrite_sdp(struct pjmedia_sdp_session *sdp)
return 0;
}
+static void sanitize_tdata(pjsip_tx_data *tdata)
+{
+ 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_fromto_hdr *fromto;
+ pjsip_contact_hdr *contact;
+ pjsip_hdr *hdr;
+
+ if (tdata->msg->type == PJSIP_REQUEST_MSG) {
+ uri = pjsip_uri_get_uri(tdata->msg->line.req.uri);
+ x_transport = pjsip_param_find(&uri->other_param, &x_name);
+ if (x_transport) {
+ pj_list_erase(x_transport);
+ }
+ }
+
+ for (hdr = tdata->msg->hdr.next; hdr != &tdata->msg->hdr; hdr = hdr->next) {
+ if (hdr->type == PJSIP_H_TO || hdr->type == PJSIP_H_FROM) {
+ fromto = (pjsip_fromto_hdr *) hdr;
+ uri = pjsip_uri_get_uri(fromto->uri);
+ x_transport = pjsip_param_find(&uri->other_param, &x_name);
+ if (x_transport) {
+ pj_list_erase(x_transport);
+ }
+ } else if (hdr->type == PJSIP_H_CONTACT) {
+ contact = (pjsip_contact_hdr *) hdr;
+ uri = pjsip_uri_get_uri(contact->uri);
+ x_transport = pjsip_param_find(&uri->other_param, &x_name);
+ if (x_transport) {
+ pj_list_erase(x_transport);
+ }
+ }
+ }
+
+ pjsip_tx_data_invalidate_msg(tdata);
+}
+
static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
{
struct multihomed_message_restrictions *restrictions = ast_sip_mod_data_get(tdata->mod_data, multihomed_module.id, MOD_DATA_RESTRICTIONS);
@@ -159,6 +199,8 @@ static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
pjsip_via_hdr *via;
pjsip_fromto_hdr *from;
+ sanitize_tdata(tdata);
+
/* 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;
@@ -273,6 +315,47 @@ static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
return PJ_SUCCESS;
}
+static pj_bool_t multihomed_on_rx_message(pjsip_rx_data *rdata)
+{
+ 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)) {
+ return PJ_FALSE;
+ }
+
+ 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;
+}
+
void ast_res_pjsip_cleanup_message_ip_updater(void)
{
ast_sip_unregister_service(&multihomed_module);
diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c
index 6fc724a1e..f935882c9 100644
--- a/res/res_pjsip_endpoint_identifier_ip.c
+++ b/res/res_pjsip_endpoint_identifier_ip.c
@@ -35,20 +35,33 @@
/*** DOCUMENTATION
<configInfo name="res_pjsip_endpoint_identifier_ip" language="en_US">
- <synopsis>Module that identifies endpoints via source IP address</synopsis>
+ <synopsis>Module that identifies endpoints</synopsis>
<configFile name="pjsip.conf">
<configObject name="identify">
- <synopsis>Identifies endpoints via source IP address</synopsis>
+ <synopsis>Identifies endpoints via some criteria.</synopsis>
+ <description>
+ <para>This module provides alternatives to matching inbound requests to
+ a configured endpoint. At least one of the matching mechanisms
+ must be provided, or the object configuration will be invalid.</para>
+ <para>If multiple criteria are provided, an inbound request will
+ be matched if it matches <emphasis>any</emphasis> of the criteria.</para>
+ <para>The matching mechanisms are provided by the following
+ configuration options:</para>
+ <enumlist>
+ <enum name="match"><para>Match by source IP address.</para></enum>
+ <enum name="match_header"><para>Match by SIP header.</para></enum>
+ </enumlist>
+ </description>
<configOption name="endpoint">
<synopsis>Name of Endpoint</synopsis>
</configOption>
<configOption name="match">
- <synopsis>IP addresses or networks to match against</synopsis>
+ <synopsis>IP addresses or networks to match against.</synopsis>
<description><para>
The value is a comma-delimited list of IP addresses. IP addresses may
have a subnet mask appended. The subnet mask may be written in either
CIDR or dot-decimal notation. Separate the IP address and subnet
- mask with a slash ('/')
+ mask with a slash ('/').
</para></description>
</configOption>
<configOption name="srv_lookups" default="yes">
@@ -57,7 +70,15 @@
perform SRV lookups for _sip._udp, _sip._tcp, and _sips._tcp of the given
hostnames to determine additional addresses that traffic may originate from.
</para></description>
- </configOption>
+ </configOption>
+ <configOption name="match_header">
+ <synopsis>Header/value pair to match against.</synopsis>
+ <description><para>A SIP header who value is used to match against. SIP
+ requests containing the header, along with the specified value, will be
+ mapped to the specified endpoint. The header must be specified with a
+ <literal>:</literal>, as in <literal>match_header = SIPHeader: value</literal>.
+ </para></description>
+ </configOption>
<configOption name="type">
<synopsis>Must be of type 'identify'.</synopsis>
</configOption>
@@ -77,6 +98,8 @@ struct ip_identify_match {
AST_DECLARE_STRING_FIELDS(
/*! The name of the endpoint */
AST_STRING_FIELD(endpoint_name);
+ /*! If matching by header, the header/value to match against */
+ AST_STRING_FIELD(match_header);
);
/*! \brief Networks or addresses that should match this */
struct ast_ha *matches;
@@ -109,7 +132,53 @@ static void *ip_identify_alloc(const char *name)
return identify;
}
-/*! \brief Comparator function for a matching object */
+/*! \brief Comparator function for matching an object by header */
+static int header_identify_match_check(void *obj, void *arg, int flags)
+{
+ struct ip_identify_match *identify = obj;
+ struct pjsip_rx_data *rdata = arg;
+ pjsip_generic_string_hdr *header;
+ pj_str_t pj_header_name;
+ pj_str_t pj_header_value;
+ char *c_header;
+ char *c_value;
+
+ if (ast_strlen_zero(identify->match_header)) {
+ return 0;
+ }
+
+ c_header = ast_strdupa(identify->match_header);
+ c_value = strchr(c_header, ':');
+ if (!c_value) {
+ ast_log(LOG_WARNING, "Identify '%s' has invalid header_match: No ':' separator found!\n",
+ ast_sorcery_object_get_id(identify));
+ return 0;
+ }
+ *c_value = '\0';
+ c_value++;
+ c_value = ast_strip(c_value);
+
+ pj_header_name = pj_str(c_header);
+ header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &pj_header_name, NULL);
+ if (!header) {
+ ast_debug(3, "SIP message does not contain header '%s'\n", c_header);
+ return 0;
+ }
+
+ pj_header_value = pj_str(c_value);
+ if (pj_strcmp(&pj_header_value, &header->hvalue)) {
+ ast_debug(3, "SIP message contains header '%s' but value '%.*s' does not match value '%s' for endpoint '%s'\n",
+ c_header,
+ (int) pj_strlen(&header->hvalue), pj_strbuf(&header->hvalue),
+ c_value,
+ identify->endpoint_name);
+ return 0;
+ }
+
+ return CMP_MATCH | CMP_STOP;
+}
+
+/*! \brief Comparator function for matching an object by IP address */
static int ip_identify_match_check(void *obj, void *arg, int flags)
{
struct ip_identify_match *identify = obj;
@@ -147,10 +216,14 @@ static struct ast_sip_endpoint *ip_identify(pjsip_rx_data *rdata)
ast_sockaddr_parse(&addr, rdata->pkt_info.src_name, PARSE_PORT_FORBID);
ast_sockaddr_set_port(&addr, rdata->pkt_info.src_port);
- if (!(match = ao2_callback(candidates, 0, ip_identify_match_check, &addr))) {
- ast_debug(3, "'%s' did not match any identify section rules\n",
+ match = ao2_callback(candidates, 0, ip_identify_match_check, &addr);
+ if (!match) {
+ ast_debug(3, "Identify checks by IP address failed to find match: '%s' did not match any identify section rules\n",
ast_sockaddr_stringify(&addr));
- return NULL;
+ match = ao2_callback(candidates, 0, header_identify_match_check, rdata);
+ if (!match) {
+ return NULL;
+ }
}
endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", match->endpoint_name);
@@ -495,7 +568,7 @@ static int cli_print_header(void *obj, void *arg, int flags)
filler = CLI_LAST_TABSTOP - indent - 24;
ast_str_append(&context->output_buffer, 0,
- "%*s: <ip/cidr%*.*s>\n",
+ "%*s: <criteria%*.*s>\n",
indent, "Match", filler, filler, CLI_HEADER_FILLER);
context->indent_level--;
@@ -532,6 +605,13 @@ static int cli_print_body(void *obj, void *arg, int flags)
addr, ast_sockaddr_cidr_bits(&match->netmask));
}
+ if (!ast_strlen_zero(ident->match_header)) {
+ ast_str_append(&context->output_buffer, 0, "%*s: %s\n",
+ indent,
+ "Match",
+ ident->match_header);
+ }
+
context->indent_level--;
if (context->indent_level == 0) {
@@ -592,6 +672,7 @@ static int load_module(void)
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "type", "", OPT_NOOP_T, 0, 0);
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, endpoint_name));
ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "identify", "match", "", ip_identify_match_handler, match_to_str, match_to_var_list, 0, 0);
+ ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "match_header", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, match_header));
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "srv_lookups", "yes", OPT_BOOL_T, 1, FLDSET(struct ip_identify_match, srv_lookups));
ast_sorcery_load_object(ast_sip_get_sorcery(), "identify");
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 7a0b60ac8..ee1894ffb 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -1087,7 +1087,7 @@ static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const c
pj_str_t tmp, local_addr;
pjsip_uri *uri;
pjsip_sip_uri *sip_uri;
- pjsip_transport_type_e type = PJSIP_TRANSPORT_UNSPECIFIED;
+ pjsip_transport_type_e type;
int local_port;
pj_strdup_with_null(pool, &tmp, target);
@@ -1099,20 +1099,20 @@ static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const c
sip_uri = pjsip_uri_get_uri(uri);
+ type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri)) {
- type = PJSIP_TRANSPORT_TLS;
+ if (type == PJSIP_TRANSPORT_UNSPECIFIED
+ || !(pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE)) {
+ type = PJSIP_TRANSPORT_TLS;
+ }
} else if (!sip_uri->transport_param.slen) {
type = PJSIP_TRANSPORT_UDP;
- } else {
- type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
- }
-
- if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
+ } else if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
return -1;
}
if (pj_strchr(&sip_uri->host, ':')) {
- type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
+ type |= PJSIP_TRANSPORT_IPV6;
}
if (pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()),
@@ -1121,7 +1121,7 @@ static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const c
}
if (!pj_strchr(&sip_uri->host, ':') && pj_strchr(&local_addr, ':')) {
- type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6);
+ type |= PJSIP_TRANSPORT_IPV6;
}
contact->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index 1892a20e9..79a4a8c3e 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -123,6 +123,9 @@
<configOption name="expires">
<synopsis>The time at which the subscription expires</synopsis>
</configOption>
+ <configOption name="contact_uri">
+ <synopsis>The Contact URI of the dialog for the subscription</synopsis>
+ </configOption>
</configObject>
<configObject name="resource_list">
<synopsis>Resource list configuration parameters.</synopsis>
@@ -376,6 +379,8 @@ struct subscription_persistence {
char *tag;
/*! When this subscription expires */
struct timeval expires;
+ /*! Contact URI */
+ char contact_uri[PJSIP_MAX_URL_SIZE];
};
/*!
@@ -591,8 +596,8 @@ static void subscription_persistence_update(struct sip_subscription_tree *sub_tr
return;
}
- ast_debug(3, "Updating persistence for '%s->%s'\n",
- ast_sorcery_object_get_id(sub_tree->endpoint), sub_tree->root->resource);
+ ast_debug(3, "Updating persistence for '%s->%s'\n", sub_tree->persistence->endpoint,
+ sub_tree->root->resource);
dlg = sub_tree->dlg;
sub_tree->persistence->cseq = dlg->local.cseq;
@@ -600,10 +605,14 @@ static void subscription_persistence_update(struct sip_subscription_tree *sub_tr
if (rdata) {
int expires;
pjsip_expires_hdr *expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
+ pjsip_contact_hdr *contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
sub_tree->persistence->expires = ast_tvadd(ast_tvnow(), ast_samp2tv(expires, 1));
+ pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, contact_hdr->uri,
+ sub_tree->persistence->contact_uri, sizeof(sub_tree->persistence->contact_uri));
+
/* When receiving a packet on an streaming transport, it's possible to receive more than one SIP
* message at a time into the rdata->pkt_info.packet buffer. However, the rdata->msg_info.msg_buf
* will always point to the proper SIP message that is to be processed. When updating subscription
@@ -1572,8 +1581,9 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags)
pj_pool_reset(pool);
rdata.tp_info.pool = pool;
- if (ast_sip_create_rdata(&rdata, persistence->packet, persistence->src_name, persistence->src_port,
- persistence->transport_key, persistence->local_name, persistence->local_port)) {
+ if (ast_sip_create_rdata_with_contact(&rdata, persistence->packet, persistence->src_name,
+ persistence->src_port, persistence->transport_key, persistence->local_name,
+ persistence->local_port, persistence->contact_uri)) {
ast_log(LOG_WARNING, "Failed recreating '%s' subscription: The message could not be parsed\n",
persistence->endpoint);
ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
@@ -1725,28 +1735,6 @@ void *ast_sip_subscription_get_header(const struct ast_sip_subscription *sub, co
return pjsip_msg_find_hdr_by_name(msg, &name, NULL);
}
-/*!
- * \internal
- * \brief Wrapper for pjsip_evsub_send_request
- *
- * This function (re)sets the transport before sending to catch cases
- * where the transport might have changed.
- *
- * If pjproject gives us the ability to resend, we'll only reset the transport
- * if PJSIP_ETPNOTAVAIL is returned from send.
- *
- * \returns pj_status_t
- */
-static pj_status_t internal_pjsip_evsub_send_request(struct sip_subscription_tree *sub_tree, pjsip_tx_data *tdata)
-{
- pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
-
- ast_sip_set_tpselector_from_transport_name(sub_tree->endpoint->transport, &selector);
- pjsip_dlg_set_transport(sub_tree->dlg, &selector);
-
- return pjsip_evsub_send_request(sub_tree->evsub, tdata);
-}
-
/* XXX This function is not used. */
struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler,
struct ast_sip_endpoint *endpoint, const char *resource)
@@ -1794,7 +1782,7 @@ struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_su
evsub = sub_tree->evsub;
if (pjsip_evsub_initiate(evsub, NULL, -1, &tdata) == PJ_SUCCESS) {
- internal_pjsip_evsub_send_request(sub_tree, tdata);
+ pjsip_evsub_send_request(sub_tree->evsub, tdata);
} else {
/* pjsip_evsub_terminate will result in pubsub_on_evsub_state,
* being called and terminating the subscription. Therefore, we don't
@@ -1891,7 +1879,7 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree,
return -1;
}
- res = internal_pjsip_evsub_send_request(sub_tree, tdata);
+ res = pjsip_evsub_send_request(sub_tree->evsub, tdata);
subscription_persistence_update(sub_tree, NULL, SUBSCRIPTION_PERSISTENCE_SEND_REQUEST);
@@ -5343,6 +5331,8 @@ static int load_module(void)
persistence_tag_str2struct, persistence_tag_struct2str, NULL, 0, 0);
ast_sorcery_object_field_register_custom(sorcery, "subscription_persistence", "expires", "",
persistence_expires_str2struct, persistence_expires_struct2str, NULL, 0, 0);
+ ast_sorcery_object_field_register(sorcery, "subscription_persistence", "contact_uri", "", OPT_CHAR_ARRAY_T, 0,
+ CHARFLDSET(struct subscription_persistence, contact_uri));
if (apply_list_configuration(sorcery)) {
ast_sip_unregister_service(&pubsub_module);
diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c
index d52a922fd..db5061249 100644
--- a/res/res_pjsip_refer.c
+++ b/res/res_pjsip_refer.c
@@ -822,6 +822,13 @@ static int refer_incoming_blind_request(struct ast_sip_session *session, pjsip_r
*/
AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(exten);
+ /* Uri without exten */
+ if (ast_strlen_zero(exten)) {
+ ast_copy_string(exten, "s", sizeof(exten));
+ ast_debug(3, "Channel '%s' from endpoint '%s' attempted blind transfer to a target without extension. Target was set to 's@%s'\n",
+ ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint), context);
+ }
+
if (!ast_exists_extension(NULL, context, exten, 1, NULL)) {
ast_log(LOG_ERROR, "Channel '%s' from endpoint '%s' attempted blind transfer to '%s@%s' but target does not exist\n",
ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint), exten, context);
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 2b31d146e..2643f75b9 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -169,6 +169,23 @@ static int rtp_check_timeout(const void *data)
return 0;
}
+/*!
+ * \brief Enable RTCP on an RTP session.
+ */
+static void enable_rtcp(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
+ const struct pjmedia_sdp_media *remote_media)
+{
+ enum ast_rtp_instance_rtcp rtcp_type;
+
+ if (session->endpoint->rtcp_mux && session_media->remote_rtcp_mux) {
+ rtcp_type = AST_RTP_INSTANCE_RTCP_MUX;
+ } else {
+ rtcp_type = AST_RTP_INSTANCE_RTCP_STANDARD;
+ }
+
+ ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RTCP, rtcp_type);
+}
+
/*! \brief Internal function which creates an RTP instance */
static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_media *session_media)
{
@@ -200,7 +217,6 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me
return -1;
}
- ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RTCP, 1);
ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_NAT, session->endpoint->media.rtp.symmetric);
if (!session->endpoint->media.rtp.ice_support && (ice = ast_rtp_instance_get_ice(session_media->rtp))) {
@@ -569,6 +585,13 @@ static void process_ice_attributes(struct ast_sip_session *session, struct ast_s
continue;
}
+ if (session->endpoint->rtcp_mux && session_media->remote_rtcp_mux && candidate.id > 1) {
+ /* Remote side may have offered RTP and RTCP candidates. However, if we're using RTCP MUX,
+ * then we should ignore RTCP candidates.
+ */
+ continue;
+ }
+
candidate.foundation = foundation;
candidate.transport = transport;
@@ -865,6 +888,26 @@ static int setup_media_encryption(struct ast_sip_session *session,
return 0;
}
+static void set_ice_components(struct ast_sip_session *session, struct ast_sip_session_media *session_media)
+{
+ struct ast_rtp_engine_ice *ice;
+
+ ast_assert(session_media->rtp != NULL);
+
+ ice = ast_rtp_instance_get_ice(session_media->rtp);
+ if (!session->endpoint->media.rtp.ice_support || !ice) {
+ return;
+ }
+
+ if (session->endpoint->rtcp_mux && session_media->remote_rtcp_mux) {
+ /* We both support RTCP mux. Only one ICE component necessary */
+ ice->change_components(session_media->rtp, 1);
+ } else {
+ /* They either don't support RTCP mux or we don't know if they do yet. */
+ ice->change_components(session_media->rtp, 2);
+ }
+}
+
/*! \brief Function which negotiates an incoming media stream */
static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream)
@@ -909,6 +952,11 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
return -1;
}
+ session_media->remote_rtcp_mux = (pjmedia_sdp_media_find_attr2(stream, "rtcp-mux", NULL) != NULL);
+ set_ice_components(session, session_media);
+
+ enable_rtcp(session, session_media, stream);
+
res = setup_media_encryption(session, session_media, sdp, stream);
if (res) {
if (!session->endpoint->media.rtp.encryption_optimistic ||
@@ -1079,6 +1127,9 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
return -1;
}
+ set_ice_components(session, session_media);
+ enable_rtcp(session, session_media, NULL);
+
if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media))) ||
!(media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)))) {
return -1;
@@ -1242,6 +1293,12 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
attr->name = STR_SENDRECV;
media->attr[media->attr_count++] = attr;
+ /* If we've got rtcp-mux enabled, just unconditionally offer it in all SDPs */
+ if (session->endpoint->rtcp_mux) {
+ attr = pjmedia_sdp_attr_create(pool, "rtcp-mux", NULL);
+ pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr);
+ }
+
/* Add the media stream to the SDP */
sdp->media[sdp->media_count++] = media;
@@ -1276,6 +1333,11 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
return -1;
}
+ session_media->remote_rtcp_mux = (pjmedia_sdp_media_find_attr2(remote_stream, "rtcp-mux", NULL) != NULL);
+ set_ice_components(session, session_media);
+
+ enable_rtcp(session, session_media, remote_stream);
+
res = setup_media_encryption(session, session_media, remote, remote_stream);
if (!session->endpoint->media.rtp.encryption_optimistic && res) {
/* If optimistic encryption is disabled and crypto should have been enabled but was not
@@ -1307,7 +1369,9 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
return -1;
}
ast_channel_set_fd(session->channel, fdno, ast_rtp_instance_fd(session_media->rtp, 0));
- ast_channel_set_fd(session->channel, fdno + 1, ast_rtp_instance_fd(session_media->rtp, 1));
+ if (!session->endpoint->rtcp_mux || !session_media->remote_rtcp_mux) {
+ ast_channel_set_fd(session->channel, fdno + 1, ast_rtp_instance_fd(session_media->rtp, 1));
+ }
/* If ICE support is enabled find all the needed attributes */
process_ice_attributes(session, session_media, remote, remote_stream);
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 3c4f102f8..98ee87209 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -973,32 +973,10 @@ int ast_sip_session_refresh(struct ast_sip_session *session,
return 0;
}
-/*!
- * \internal
- * \brief Wrapper for pjsip_inv_send_msg
- *
- * This function (re)sets the transport before sending to catch cases
- * where the transport might have changed.
- *
- * If pjproject gives us the ability to resend, we'll only reset the transport
- * if PJSIP_ETPNOTAVAIL is returned from send.
- *
- * \returns pj_status_t
- */
-static pj_status_t internal_pjsip_inv_send_msg(pjsip_inv_session *inv, const char *transport_name, pjsip_tx_data *tdata)
-{
- pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
-
- ast_sip_set_tpselector_from_transport_name(transport_name, &selector);
- pjsip_dlg_set_transport(inv->dlg, &selector);
-
- return pjsip_inv_send_msg(inv, tdata);
-}
-
void ast_sip_session_send_response(struct ast_sip_session *session, pjsip_tx_data *tdata)
{
handle_outgoing_response(session, tdata);
- internal_pjsip_inv_send_msg(session->inv_session, session->endpoint->transport, tdata);
+ pjsip_inv_send_msg(session->inv_session, tdata);
return;
}
@@ -1229,7 +1207,7 @@ void ast_sip_session_send_request_with_cb(struct ast_sip_session *session, pjsip
MOD_DATA_ON_RESPONSE, on_response);
handle_outgoing_request(session, tdata);
- internal_pjsip_inv_send_msg(session->inv_session, session->endpoint->transport, tdata);
+ pjsip_inv_send_msg(session->inv_session, tdata);
return;
}
@@ -2049,7 +2027,7 @@ static pjsip_inv_session *pre_session_setup(pjsip_rx_data *rdata, const struct a
if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) != PJ_SUCCESS) {
pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
}
- internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata);
+ pjsip_inv_send_msg(inv_session, tdata);
return NULL;
}
return inv_session;
@@ -2218,7 +2196,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) {
pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
} else {
- internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata);
+ pjsip_inv_send_msg(inv_session, tdata);
}
}
return;
@@ -2230,7 +2208,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) {
pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
} else {
- internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata);
+ pjsip_inv_send_msg(inv_session, tdata);
}
#ifdef HAVE_PJSIP_INV_SESSION_REF
pjsip_inv_dec_ref(inv_session);
@@ -2243,7 +2221,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) {
pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
} else {
- internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata);
+ pjsip_inv_send_msg(inv_session, tdata);
}
#ifdef HAVE_PJSIP_INV_SESSION_REF
pjsip_inv_dec_ref(inv_session);
diff --git a/res/res_pjsip_transport_websocket.c b/res/res_pjsip_transport_websocket.c
index ff8e346f4..1b9d616de 100644
--- a/res/res_pjsip_transport_websocket.c
+++ b/res/res_pjsip_transport_websocket.c
@@ -39,6 +39,7 @@
#include "asterisk/taskprocessor.h"
static int transport_type_wss;
+static int transport_type_wss_ipv6;
/*!
* \brief Wrapper for pjsip_transport, for storing the WebSocket session
@@ -198,15 +199,20 @@ static int transport_create(void *data)
newtransport->transport.type_name, ws_addr_str);
pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ws_addr_str), &newtransport->transport.key.rem_addr);
- newtransport->transport.key.rem_addr.addr.sa_family = pj_AF_INET();
- newtransport->transport.key.type = transport_type_wss;
+ if (newtransport->transport.key.rem_addr.addr.sa_family == pj_AF_INET6()) {
+ newtransport->transport.key.type = transport_type_wss_ipv6;
+ newtransport->transport.local_name.host.ptr = (char *)pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN);
+ pj_sockaddr_print(&newtransport->transport.key.rem_addr, newtransport->transport.local_name.host.ptr, PJ_INET6_ADDRSTRLEN, 0);
+ } else {
+ newtransport->transport.key.type = transport_type_wss;
+ newtransport->transport.local_name.host.ptr = (char *)pj_pool_alloc(pool, PJ_INET_ADDRSTRLEN);
+ pj_sockaddr_print(&newtransport->transport.key.rem_addr, newtransport->transport.local_name.host.ptr, PJ_INET_ADDRSTRLEN, 0);
+ }
newtransport->transport.addr_len = pj_sockaddr_get_len(&newtransport->transport.key.rem_addr);
pj_sockaddr_cp(&newtransport->transport.local_addr, &newtransport->transport.key.rem_addr);
- newtransport->transport.local_name.host.ptr = (char *)pj_pool_alloc(pool, newtransport->transport.addr_len+4);
- pj_sockaddr_print(&newtransport->transport.key.rem_addr, newtransport->transport.local_name.host.ptr, newtransport->transport.addr_len+4, 0);
newtransport->transport.local_name.host.slen = pj_ansi_strlen(newtransport->transport.local_name.host.ptr);
newtransport->transport.local_name.port = pj_sockaddr_get_port(&newtransport->transport.key.rem_addr);
@@ -271,8 +277,6 @@ static int transport_read(void *data)
rdata->pkt_info.zero = 0;
pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ast_sockaddr_stringify(ast_websocket_remote_address(session))), &rdata->pkt_info.src_addr);
- rdata->pkt_info.src_addr.addr.sa_family = pj_AF_INET();
-
rdata->pkt_info.src_addr_len = sizeof(rdata->pkt_info.src_addr);
pj_ansi_strcpy(rdata->pkt_info.src_name, ast_sockaddr_stringify_host(ast_websocket_remote_address(session)));
@@ -395,7 +399,7 @@ static pj_bool_t websocket_on_rx_msg(pjsip_rx_data *rdata)
long type = rdata->tp_info.transport->key.type;
- if (type != (long) transport_type_wss) {
+ if (type != (long) transport_type_wss && type != (long) transport_type_wss_ipv6) {
return PJ_FALSE;
}
@@ -451,15 +455,17 @@ static int load_module(void)
CHECK_PJSIP_MODULE_LOADED();
/*
- * We only need one transport type defined. Firefox and Chrome
- * do not support anything other than secure websockets anymore.
+ * We only need one transport type name (ws) defined. Firefox
+ * and Chrome do not support anything other than secure websockets
+ * anymore.
*
* Also we really cannot have two transports with the same name
- * because it would be ambiguous. Outgoing requests may try to
- * find the transport by name and pjproject only finds the first
- * one registered.
+ * and address family because it would be ambiguous. Outgoing
+ * requests may try to find the transport by name and pjproject
+ * only finds the first one registered.
*/
pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE, "ws", 5060, &transport_type_wss);
+ pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_IPV6, "ws", 5060, &transport_type_wss_ipv6);
if (ast_sip_register_service(&websocket_module) != PJ_SUCCESS) {
return AST_MODULE_LOAD_DECLINE;
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 346db604c..62fe4fd4a 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -331,6 +331,7 @@ struct ast_rtp {
struct ao2_container *ice_active_remote_candidates; /*!< The remote ICE candidates */
struct ao2_container *ice_proposed_remote_candidates; /*!< Incoming remote ICE candidates for new session */
struct ast_sockaddr ice_original_rtp_addr; /*!< rtp address that ICE started on first session */
+ unsigned int ice_num_components; /*!< The number of ICE components */
#endif
#ifdef HAVE_OPENSSL_SRTP
@@ -419,6 +420,7 @@ struct ast_rtcp {
* own address every time
*/
char *local_addr_str;
+ enum ast_rtp_instance_rtcp type;
};
struct rtp_red {
@@ -660,6 +662,22 @@ static int ice_reset_session(struct ast_rtp_instance *instance)
pj_ice_sess_change_role(rtp->ice, role);
}
+ /* If we only have one component now, and we previously set up TURN for RTCP,
+ * we need to destroy that TURN socket.
+ */
+ if (rtp->ice_num_components == 1 && rtp->turn_rtcp) {
+ struct timeval wait = ast_tvadd(ast_tvnow(), ast_samp2tv(TURN_STATE_WAIT_TIME, 1000));
+ struct timespec ts = { .tv_sec = wait.tv_sec, .tv_nsec = wait.tv_usec * 1000, };
+
+ ast_mutex_lock(&rtp->lock);
+ pj_turn_sock_destroy(rtp->turn_rtcp);
+ rtp->turn_state = PJ_TURN_STATE_NULL;
+ while (rtp->turn_state != PJ_TURN_STATE_DESTROYING) {
+ ast_cond_timedwait(&rtp->cond, &rtp->lock, &ts);
+ }
+ ast_mutex_unlock(&rtp->lock);
+ }
+
return res;
}
@@ -775,11 +793,12 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
ast_log(LOG_WARNING, "No RTP candidates; skipping ICE checklist (%p)\n", instance);
}
- if (!has_rtcp) {
+ /* If we're only dealing with one ICE component, then we don't care about the lack of RTCP candidates */
+ if (!has_rtcp && rtp->ice_num_components > 1) {
ast_log(LOG_WARNING, "No RTCP candidates; skipping ICE checklist (%p)\n", instance);
}
- if (has_rtp && has_rtcp) {
+ if (has_rtp && (has_rtcp || rtp->ice_num_components == 1)) {
pj_status_t res = pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, cand_cnt, &candidates[0]);
char reason[80];
@@ -1271,6 +1290,21 @@ static char *generate_random_string(char *buf, size_t size)
return buf;
}
+static void ast_rtp_ice_change_components(struct ast_rtp_instance *instance, int num_components)
+{
+ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+ /* Don't do anything if ICE is unsupported or if we're not changing the
+ * number of components
+ */
+ if (!icesupport || !rtp->ice || rtp->ice_num_components == num_components) {
+ return;
+ }
+
+ rtp->ice_num_components = num_components;
+ ice_reset_session(instance);
+}
+
/* ICE RTP Engine interface declaration */
static struct ast_rtp_engine_ice ast_rtp_ice = {
.set_authentication = ast_rtp_ice_set_authentication,
@@ -1283,6 +1317,7 @@ static struct ast_rtp_engine_ice ast_rtp_ice = {
.ice_lite = ast_rtp_ice_lite,
.set_role = ast_rtp_ice_set_role,
.turn_request = ast_rtp_ice_turn_request,
+ .change_components = ast_rtp_ice_change_components,
};
#endif
@@ -1542,6 +1577,7 @@ static int ast_rtp_dtls_active(struct ast_rtp_instance *instance)
static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance)
{
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+ int rtcp_dtls_unique = (rtp->dtls.ssl != rtp->rtcp->dtls.ssl);
dtls_srtp_stop_timeout_timer(instance, rtp, 0);
@@ -1559,7 +1595,7 @@ static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance)
if (rtp->rtcp) {
dtls_srtp_stop_timeout_timer(instance, rtp, 1);
- if (rtp->rtcp->dtls.ssl) {
+ if (rtp->rtcp->dtls.ssl && rtcp_dtls_unique) {
SSL_free(rtp->rtcp->dtls.ssl);
rtp->rtcp->dtls.ssl = NULL;
ast_mutex_destroy(&rtp->rtcp->dtls.lock);
@@ -1787,7 +1823,7 @@ static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)
#ifdef HAVE_OPENSSL_SRTP
dtls_perform_handshake(instance, &rtp->dtls, 0);
- if (rtp->rtcp) {
+ if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1);
}
#endif
@@ -2027,7 +2063,7 @@ static int dtls_srtp_renegotiate(const void *data)
SSL_do_handshake(rtp->dtls.ssl);
dtls_srtp_check_pending(instance, rtp, 0);
- if (rtp->rtcp && rtp->rtcp->dtls.ssl) {
+ if (rtp->rtcp && rtp->rtcp->dtls.ssl && rtp->rtcp->dtls.ssl != rtp->dtls.ssl) {
SSL_renegotiate(rtp->rtcp->dtls.ssl);
SSL_do_handshake(rtp->rtcp->dtls.ssl);
dtls_srtp_check_pending(instance, rtp, 1);
@@ -2618,7 +2654,7 @@ static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *ad
passwd = pj_str(rtp->local_passwd);
/* Create an ICE session for ICE negotiation */
- if (pj_ice_sess_create(&stun_config, NULL, PJ_ICE_SESS_ROLE_UNKNOWN, 2,
+ if (pj_ice_sess_create(&stun_config, NULL, PJ_ICE_SESS_ROLE_UNKNOWN, rtp->ice_num_components,
&ast_rtp_ice_sess_cb, &ufrag, &passwd, NULL, &rtp->ice) == PJ_SUCCESS) {
/* Make this available for the callbacks */
rtp->ice->user_data = instance;
@@ -2627,9 +2663,10 @@ static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *ad
rtp_add_candidates_to_ice(instance, rtp, addr, port, AST_RTP_ICE_COMPONENT_RTP,
TRANSPORT_SOCKET_RTP);
- /* Only add the RTCP candidates to ICE when replacing the session. New sessions
+ /* Only add the RTCP candidates to ICE when replacing the session and if
+ * the ICE session contains more than just an RTP component. New sessions
* handle this in a separate part of the setup phase */
- if (replace && rtp->rtcp) {
+ if (replace && rtp->rtcp && rtp->ice_num_components > 1) {
rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us,
ast_sockaddr_port(&rtp->rtcp->us), AST_RTP_ICE_COMPONENT_RTCP,
TRANSPORT_SOCKET_RTCP);
@@ -2714,6 +2751,7 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
#ifdef HAVE_PJPROJECT
/* Create an ICE session for ICE negotiation */
if (icesupport) {
+ rtp->ice_num_components = 2;
ast_debug(3, "Creating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(addr), x, instance);
if (ice_create(instance, addr, x, 0)) {
ast_log(LOG_NOTICE, "Failed to start ICE session\n");
@@ -2723,7 +2761,6 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
}
}
#endif
-
/* Record any information we may need */
rtp->sched = sched;
@@ -4154,63 +4191,21 @@ static void update_lost_stats(struct ast_rtp *rtp, unsigned int lost_packets)
rtp->rtcp->reported_normdev_lost = reported_normdev_lost_current;
}
-static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
+static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, const unsigned char *rtcpdata, size_t size, struct ast_sockaddr *addr)
{
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
- struct ast_sockaddr addr;
- unsigned char rtcpdata[8192 + AST_FRIENDLY_OFFSET];
- unsigned int *rtcpheader = (unsigned int *)(rtcpdata + AST_FRIENDLY_OFFSET);
- int res, packetwords, position = 0;
+ unsigned int *rtcpheader = (unsigned int *)(rtcpdata);
+ int packetwords, position = 0;
int report_counter = 0;
struct ast_rtp_rtcp_report_block *report_block;
struct ast_frame *f = &ast_null_frame;
- /* Read in RTCP data from the socket */
- if ((res = rtcp_recvfrom(instance, rtcpdata + AST_FRIENDLY_OFFSET,
- sizeof(rtcpdata) - AST_FRIENDLY_OFFSET,
- 0, &addr)) < 0) {
- ast_assert(errno != EBADF);
- if (errno != EAGAIN) {
- ast_log(LOG_WARNING, "RTCP Read error: %s. Hanging up.\n",
- (errno) ? strerror(errno) : "Unspecified");
- return NULL;
- }
- return &ast_null_frame;
- }
-
- /* If this was handled by the ICE session don't do anything further */
- if (!res) {
- return &ast_null_frame;
- }
-
- if (!*(rtcpdata + AST_FRIENDLY_OFFSET)) {
- struct sockaddr_in addr_tmp;
- struct ast_sockaddr addr_v4;
-
- if (ast_sockaddr_is_ipv4(&addr)) {
- ast_sockaddr_to_sin(&addr, &addr_tmp);
- } else if (ast_sockaddr_ipv4_mapped(&addr, &addr_v4)) {
- ast_debug(1, "Using IPv6 mapped address %s for STUN\n",
- ast_sockaddr_stringify(&addr));
- ast_sockaddr_to_sin(&addr_v4, &addr_tmp);
- } else {
- ast_debug(1, "Cannot do STUN for non IPv4 address %s\n",
- ast_sockaddr_stringify(&addr));
- return &ast_null_frame;
- }
- if ((ast_stun_handle_packet(rtp->rtcp->s, &addr_tmp, rtcpdata + AST_FRIENDLY_OFFSET, res, NULL, NULL) == AST_STUN_ACCEPT)) {
- ast_sockaddr_from_sin(&addr, &addr_tmp);
- ast_sockaddr_copy(&rtp->rtcp->them, &addr);
- }
- return &ast_null_frame;
- }
-
- packetwords = res / 4;
+ packetwords = size / 4;
if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) {
/* Send to whoever sent to us */
- if (ast_sockaddr_cmp(&rtp->rtcp->them, &addr)) {
- ast_sockaddr_copy(&rtp->rtcp->them, &addr);
+ if (ast_sockaddr_cmp(&rtp->rtcp->them, addr)) {
+ ast_sockaddr_copy(&rtp->rtcp->them, addr);
if (rtpdebug) {
ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s\n",
ast_sockaddr_stringify(&rtp->rtcp->them));
@@ -4218,7 +4213,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
}
}
- ast_debug(1, "Got RTCP report of %d bytes\n", res);
+ ast_debug(1, "Got RTCP report of %zu bytes\n", size);
while (position < packetwords) {
int i, pt, rc;
@@ -4246,9 +4241,9 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
return &ast_null_frame;
}
- if (rtcp_debug_test_addr(&addr)) {
+ if (rtcp_debug_test_addr(addr)) {
ast_verbose("\n\nGot RTCP from %s\n",
- ast_sockaddr_stringify(&addr));
+ ast_sockaddr_stringify(addr));
ast_verbose("PT: %d(%s)\n", pt, (pt == RTCP_PT_SR) ? "Sender Report" :
(pt == RTCP_PT_RR) ? "Receiver Report" :
(pt == RTCP_PT_FUR) ? "H.261 FUR" : "Unknown");
@@ -4271,7 +4266,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
(unsigned int)ntohl(rtcpheader[i + 1]),
&rtcp_report->sender_information.ntp_timestamp);
rtcp_report->sender_information.rtp_timestamp = ntohl(rtcpheader[i + 2]);
- if (rtcp_debug_test_addr(&addr)) {
+ if (rtcp_debug_test_addr(addr)) {
ast_verbose("NTP timestamp: %u.%06u\n",
(unsigned int)rtcp_report->sender_information.ntp_timestamp.tv_sec,
(unsigned int)rtcp_report->sender_information.ntp_timestamp.tv_usec);
@@ -4303,7 +4298,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
report_block->dlsr = ntohl(rtcpheader[i + 5]);
if (report_block->lsr
&& update_rtt_stats(rtp, report_block->lsr, report_block->dlsr)
- && rtcp_debug_test_addr(&addr)) {
+ && rtcp_debug_test_addr(addr)) {
struct timeval now;
unsigned int lsr_now, lsw, msw;
gettimeofday(&now, NULL);
@@ -4320,7 +4315,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
update_lost_stats(rtp, report_block->lost_count.packets);
rtp->rtcp->reported_jitter_count++;
- if (rtcp_debug_test_addr(&addr)) {
+ if (rtcp_debug_test_addr(addr)) {
ast_verbose(" Fraction lost: %d\n", report_block->lost_count.fraction);
ast_verbose(" Packets lost so far: %u\n", report_block->lost_count.packets);
ast_verbose(" Highest sequence number: %u\n", report_block->highest_seq_no & 0x0000ffff);
@@ -4348,7 +4343,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
case RTCP_PT_FUR:
/* Handle RTCP FIR as FUR */
case RTCP_PT_PSFB:
- if (rtcp_debug_test_addr(&addr)) {
+ if (rtcp_debug_test_addr(addr)) {
ast_verbose("Received an RTCP Fast Update Request\n");
}
rtp->f.frametype = AST_FRAME_CONTROL;
@@ -4360,13 +4355,13 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
f = &rtp->f;
break;
case RTCP_PT_SDES:
- if (rtcp_debug_test_addr(&addr)) {
+ if (rtcp_debug_test_addr(addr)) {
ast_verbose("Received an SDES from %s\n",
ast_sockaddr_stringify(&rtp->rtcp->them));
}
break;
case RTCP_PT_BYE:
- if (rtcp_debug_test_addr(&addr)) {
+ if (rtcp_debug_test_addr(addr)) {
ast_verbose("Received a BYE from %s\n",
ast_sockaddr_stringify(&rtp->rtcp->them));
}
@@ -4381,6 +4376,58 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
rtp->rtcp->rtcp_info = 1;
return f;
+
+}
+
+static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
+{
+ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+ struct ast_sockaddr addr;
+ unsigned char rtcpdata[8192 + AST_FRIENDLY_OFFSET];
+ unsigned char *read_area = rtcpdata + AST_FRIENDLY_OFFSET;
+ size_t read_area_size = sizeof(rtcpdata) - AST_FRIENDLY_OFFSET;
+ int res;
+
+ /* Read in RTCP data from the socket */
+ if ((res = rtcp_recvfrom(instance, read_area, read_area_size,
+ 0, &addr)) < 0) {
+ ast_assert(errno != EBADF);
+ if (errno != EAGAIN) {
+ ast_log(LOG_WARNING, "RTCP Read error: %s. Hanging up.\n",
+ (errno) ? strerror(errno) : "Unspecified");
+ return NULL;
+ }
+ return &ast_null_frame;
+ }
+
+ /* If this was handled by the ICE session don't do anything further */
+ if (!res) {
+ return &ast_null_frame;
+ }
+
+ if (!*(read_area)) {
+ struct sockaddr_in addr_tmp;
+ struct ast_sockaddr addr_v4;
+
+ if (ast_sockaddr_is_ipv4(&addr)) {
+ ast_sockaddr_to_sin(&addr, &addr_tmp);
+ } else if (ast_sockaddr_ipv4_mapped(&addr, &addr_v4)) {
+ ast_debug(1, "Using IPv6 mapped address %s for STUN\n",
+ ast_sockaddr_stringify(&addr));
+ ast_sockaddr_to_sin(&addr_v4, &addr_tmp);
+ } else {
+ ast_debug(1, "Cannot do STUN for non IPv4 address %s\n",
+ ast_sockaddr_stringify(&addr));
+ return &ast_null_frame;
+ }
+ if ((ast_stun_handle_packet(rtp->rtcp->s, &addr_tmp, read_area, res, NULL, NULL) == AST_STUN_ACCEPT)) {
+ ast_sockaddr_from_sin(&addr, &addr_tmp);
+ ast_sockaddr_copy(&rtp->rtcp->them, &addr);
+ }
+ return &ast_null_frame;
+ }
+
+ return ast_rtcp_interpret(instance, read_area, read_area_size, &addr);
}
static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int *rtpheader, int len, int hdrlen)
@@ -4487,19 +4534,54 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int
return 0;
}
+static int rtcp_mux(struct ast_rtp *rtp, const unsigned char *packet)
+{
+ uint8_t version;
+ uint8_t pt;
+ uint8_t m;
+
+ if (!rtp->rtcp || rtp->rtcp->type != AST_RTP_INSTANCE_RTCP_MUX) {
+ return 0;
+ }
+
+ version = (packet[0] & 0XC0) >> 6;
+ if (version == 0) {
+ /* version 0 indicates this is a STUN packet and shouldn't
+ * be interpreted as a possible RTCP packet
+ */
+ return 0;
+ }
+
+ /* The second octet of a packet will be one of the following:
+ * For RTP: The marker bit (1 bit) and the RTP payload type (7 bits)
+ * For RTCP: The payload type (8)
+ *
+ * RTP has a forbidden range of payload types (64-95) since these
+ * will conflict with RTCP payload numbers if the marker bit is set.
+ */
+ m = packet[1] & 0x80;
+ pt = packet[1] & 0x7F;
+ if (m && pt >= 64 && pt <= 95) {
+ return 1;
+ }
+ return 0;
+}
+
static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtcp)
{
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
struct ast_sockaddr addr;
int res, hdrlen = 12, version, payloadtype, padding, mark, ext, cc, prev_seqno;
- unsigned int *rtpheader = (unsigned int*)(rtp->rawdata + AST_FRIENDLY_OFFSET), seqno, ssrc, timestamp;
+ unsigned char *read_area = rtp->rawdata + AST_FRIENDLY_OFFSET;
+ size_t read_area_size = sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET;
+ unsigned int *rtpheader = (unsigned int*)(read_area), seqno, ssrc, timestamp;
RAII_VAR(struct ast_rtp_payload_type *, payload, NULL, ao2_cleanup);
struct ast_sockaddr remote_address = { {0,} };
struct frame_list frames;
/* If this is actually RTCP let's hop on over and handle it */
if (rtcp) {
- if (rtp->rtcp) {
+ if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
return ast_rtcp_read(instance);
}
return &ast_null_frame;
@@ -4511,8 +4593,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
}
/* Actually read in the data from the socket */
- if ((res = rtp_recvfrom(instance, rtp->rawdata + AST_FRIENDLY_OFFSET,
- sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0,
+ if ((res = rtp_recvfrom(instance, read_area, read_area_size, 0,
&addr)) < 0) {
ast_assert(errno != EBADF);
if (errno != EAGAIN) {
@@ -4528,12 +4609,17 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
return &ast_null_frame;
}
+ /* This could be a multiplexed RTCP packet. If so, be sure to interpret it correctly */
+ if (rtcp_mux(rtp, read_area)) {
+ return ast_rtcp_interpret(instance, read_area, read_area_size, &addr);
+ }
+
/* Make sure the data that was read in is actually enough to make up an RTP packet */
if (res < hdrlen) {
/* If this is a keepalive containing only nulls, don't bother with a warning */
int i;
for (i = 0; i < res; ++i) {
- if (rtp->rawdata[AST_FRIENDLY_OFFSET + i] != '\0') {
+ if (read_area[i] != '\0') {
ast_log(LOG_WARNING, "RTP Read too short\n");
return &ast_null_frame;
}
@@ -4560,7 +4646,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
ast_sockaddr_stringify(&addr));
return &ast_null_frame;
}
- if ((ast_stun_handle_packet(rtp->s, &addr_tmp, rtp->rawdata + AST_FRIENDLY_OFFSET, res, NULL, NULL) == AST_STUN_ACCEPT) &&
+ if ((ast_stun_handle_packet(rtp->s, &addr_tmp, read_area, res, NULL, NULL) == AST_STUN_ACCEPT) &&
ast_sockaddr_isnull(&remote_address)) {
ast_sockaddr_from_sin(&addr, &addr_tmp);
ast_rtp_instance_set_remote_address(instance, &addr);
@@ -4609,7 +4695,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
/* do not update the originally given address, but only the remote */
ast_rtp_instance_set_incoming_source_address(instance, &addr);
ast_sockaddr_copy(&remote_address, &addr);
- if (rtp->rtcp) {
+ if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
ast_sockaddr_copy(&rtp->rtcp->them, &addr);
ast_sockaddr_set_port(&rtp->rtcp->them, ast_sockaddr_port(&addr) + 1);
}
@@ -4676,7 +4762,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
/* Remove any padding bytes that may be present */
if (padding) {
- res -= rtp->rawdata[AST_FRIENDLY_OFFSET + res - 1];
+ res -= read_area[res - 1];
}
/* Skip over any CSRC fields */
@@ -4750,11 +4836,11 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
* by passing the pointer to the frame list to it so that the method
* can append frames to the list as needed.
*/
- process_dtmf_rfc2833(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark, &frames);
+ process_dtmf_rfc2833(instance, read_area + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark, &frames);
} else if (payload->rtp_code == AST_RTP_CISCO_DTMF) {
- f = process_dtmf_cisco(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark);
+ f = process_dtmf_cisco(instance, read_area + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark);
} else if (payload->rtp_code == AST_RTP_CN) {
- f = process_cn_rfc3389(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark);
+ f = process_cn_rfc3389(instance, read_area + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark);
} else {
ast_log(LOG_NOTICE, "Unknown RTP codec %d received from '%s'\n",
payloadtype,
@@ -4810,7 +4896,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
rtp->f.src = "RTP";
rtp->f.mallocd = 0;
rtp->f.datalen = res - hdrlen;
- rtp->f.data.ptr = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET;
+ rtp->f.data.ptr = read_area + hdrlen;
rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET;
rtp->f.seqno = seqno;
@@ -4921,19 +5007,29 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
if (value) {
struct ast_sockaddr local_addr;
- if (rtp->rtcp) {
+ if (rtp->rtcp && rtp->rtcp->type == value) {
ast_debug(1, "Ignoring duplicate RTCP property on RTP instance '%p'\n", instance);
return;
}
- /* Setup RTCP to be activated on the next RTP write */
- if (!(rtp->rtcp = ast_calloc(1, sizeof(*rtp->rtcp)))) {
- return;
+
+ if (!rtp->rtcp) {
+ rtp->rtcp = ast_calloc(1, sizeof(*rtp->rtcp));
+ if (!rtp->rtcp) {
+ return;
+ }
+ rtp->rtcp->s = -1;
+ rtp->rtcp->dtls.timeout_timer = -1;
+ rtp->rtcp->schedid = -1;
}
+ rtp->rtcp->type = value;
+
/* Grab the IP address and port we are going to use */
ast_rtp_instance_get_local_address(instance, &rtp->rtcp->us);
- ast_sockaddr_set_port(&rtp->rtcp->us,
- ast_sockaddr_port(&rtp->rtcp->us) + 1);
+ if (value == AST_RTP_INSTANCE_RTCP_STANDARD) {
+ ast_sockaddr_set_port(&rtp->rtcp->us,
+ ast_sockaddr_port(&rtp->rtcp->us) + 1);
+ }
ast_sockaddr_copy(&local_addr, &rtp->rtcp->us);
if (!ast_find_ourip(&local_addr, &rtp->rtcp->us, 0)) {
@@ -4943,6 +5039,7 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
ast_sockaddr_copy(&local_addr, &rtp->rtcp->us);
}
+ ast_free(rtp->rtcp->local_addr_str);
rtp->rtcp->local_addr_str = ast_strdup(ast_sockaddr_stringify(&local_addr));
if (!rtp->rtcp->local_addr_str) {
ast_free(rtp->rtcp);
@@ -4950,43 +5047,67 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
return;
}
- if ((rtp->rtcp->s =
- create_new_socket("RTCP",
- ast_sockaddr_is_ipv4(&rtp->rtcp->us) ?
- AF_INET :
- ast_sockaddr_is_ipv6(&rtp->rtcp->us) ?
- AF_INET6 : -1)) < 0) {
- ast_debug(1, "Failed to create a new socket for RTCP on instance '%p'\n", instance);
- ast_free(rtp->rtcp->local_addr_str);
- ast_free(rtp->rtcp);
- rtp->rtcp = NULL;
- return;
- }
-
- /* Try to actually bind to the IP address and port we are going to use for RTCP, if this fails we have to bail out */
- if (ast_bind(rtp->rtcp->s, &rtp->rtcp->us)) {
- ast_debug(1, "Failed to setup RTCP on RTP instance '%p'\n", instance);
- close(rtp->rtcp->s);
- ast_free(rtp->rtcp->local_addr_str);
- ast_free(rtp->rtcp);
- rtp->rtcp = NULL;
- return;
- }
-
- ast_debug(1, "Setup RTCP on RTP instance '%p'\n", instance);
- rtp->rtcp->schedid = -1;
+ if (value == AST_RTP_INSTANCE_RTCP_STANDARD) {
+ /* We're either setting up RTCP from scratch or
+ * switching from MUX. Either way, we won't have
+ * a socket set up, and we need to set it up
+ */
+ if ((rtp->rtcp->s =
+ create_new_socket("RTCP",
+ ast_sockaddr_is_ipv4(&rtp->rtcp->us) ?
+ AF_INET :
+ ast_sockaddr_is_ipv6(&rtp->rtcp->us) ?
+ AF_INET6 : -1)) < 0) {
+ ast_debug(1, "Failed to create a new socket for RTCP on instance '%p'\n", instance);
+ ast_free(rtp->rtcp->local_addr_str);
+ ast_free(rtp->rtcp);
+ rtp->rtcp = NULL;
+ return;
+ }
+ /* Try to actually bind to the IP address and port we are going to use for RTCP, if this fails we have to bail out */
+ if (ast_bind(rtp->rtcp->s, &rtp->rtcp->us)) {
+ ast_debug(1, "Failed to setup RTCP on RTP instance '%p'\n", instance);
+ close(rtp->rtcp->s);
+ ast_free(rtp->rtcp->local_addr_str);
+ ast_free(rtp->rtcp);
+ rtp->rtcp = NULL;
+ return;
+ }
#ifdef HAVE_PJPROJECT
- if (rtp->ice) {
- rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us, ast_sockaddr_port(&rtp->rtcp->us), AST_RTP_ICE_COMPONENT_RTCP, TRANSPORT_SOCKET_RTCP);
- }
+ if (rtp->ice) {
+ rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us, ast_sockaddr_port(&rtp->rtcp->us), AST_RTP_ICE_COMPONENT_RTCP, TRANSPORT_SOCKET_RTCP);
+ }
#endif
-
#ifdef HAVE_OPENSSL_SRTP
- rtp->rtcp->dtls.timeout_timer = -1;
- dtls_setup_rtcp(instance);
+ dtls_setup_rtcp(instance);
#endif
+ } else {
+ struct ast_sockaddr addr;
+ /* RTCPMUX uses the same socket as RTP. If we were previously using standard RTCP
+ * then close the socket we previously created.
+ *
+ * It may seem as though there is a possible race condition here where we might try
+ * to close the RTCP socket while it is being used to send data. However, this is not
+ * a problem in practice since setting and adjusting of RTCP properties happens prior
+ * to activating RTP. It is not until RTP is activated that timers start for RTCP
+ * transmission
+ */
+ if (rtp->rtcp->s > -1) {
+ close(rtp->rtcp->s);
+ }
+ rtp->rtcp->s = rtp->s;
+ ast_rtp_instance_get_remote_address(instance, &addr);
+ ast_sockaddr_copy(&rtp->rtcp->them, &addr);
+#ifdef HAVE_OPENSSL_SRTP
+ if (rtp->rtcp->dtls.ssl) {
+ SSL_free(rtp->rtcp->dtls.ssl);
+ }
+ rtp->rtcp->dtls.ssl = rtp->dtls.ssl;
+#endif
+ }
+ ast_debug(1, "Setup RTCP on RTP instance '%p'\n", instance);
return;
} else {
if (rtp->rtcp) {
@@ -5001,9 +5122,11 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
}
rtp->rtcp->schedid = -1;
}
- close(rtp->rtcp->s);
+ if (rtp->rtcp->s > -1 && rtp->rtcp->s != rtp->s) {
+ close(rtp->rtcp->s);
+ }
#ifdef HAVE_OPENSSL_SRTP
- if (rtp->rtcp->dtls.ssl) {
+ if (rtp->rtcp->dtls.ssl && rtp->rtcp->dtls.ssl != rtp->dtls.ssl) {
SSL_free(rtp->rtcp->dtls.ssl);
}
#endif
@@ -5045,10 +5168,12 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct
ast_debug(1, "Setting RTCP address on RTP instance '%p'\n", instance);
ast_sockaddr_copy(&rtp->rtcp->them, addr);
if (!ast_sockaddr_isnull(addr)) {
- ast_sockaddr_set_port(&rtp->rtcp->them, ast_sockaddr_port(addr) + 1);
+ if (rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
+ ast_sockaddr_set_port(&rtp->rtcp->them, ast_sockaddr_port(addr) + 1);
- /* Update the local RTCP address with what is being used */
- ast_sockaddr_set_port(&local, ast_sockaddr_port(&local) + 1);
+ /* Update the local RTCP address with what is being used */
+ ast_sockaddr_set_port(&local, ast_sockaddr_port(&local) + 1);
+ }
ast_sockaddr_copy(&rtp->rtcp->us, &local);
ast_free(rtp->rtcp->local_addr_str);
@@ -5336,7 +5461,7 @@ static int ast_rtp_activate(struct ast_rtp_instance *instance)
dtls_perform_handshake(instance, &rtp->dtls, 0);
- if (rtp->rtcp) {
+ if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1);
}