|
|
@ -18,6 +18,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
#include "asterisk.h"
|
|
|
|
#include "asterisk.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
#include <pjsip.h>
|
|
|
|
#include <pjsip.h>
|
|
|
|
#include <pjlib.h>
|
|
|
|
#include <pjlib.h>
|
|
|
|
|
|
|
|
|
|
|
@ -347,6 +348,44 @@ static void copy_state_to_transport(struct ast_sip_transport *transport)
|
|
|
|
memcpy(&transport->external_address, &transport->state->external_address, sizeof(transport->external_address));
|
|
|
|
memcpy(&transport->external_address, &transport->state->external_address, sizeof(transport->external_address));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int has_state_changed(struct ast_sip_transport_state *a, struct ast_sip_transport_state *b)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (a->type != b->type) {
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (pj_sockaddr_cmp(&a->host, &b->host)) {
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((a->localnet || b->localnet)
|
|
|
|
|
|
|
|
&& ((!a->localnet != !b->localnet)
|
|
|
|
|
|
|
|
|| ast_sockaddr_cmp(&a->localnet->addr, &b->localnet->addr)
|
|
|
|
|
|
|
|
|| ast_sockaddr_cmp(&a->localnet->netmask, &b->localnet->netmask)))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (ast_sockaddr_cmp(&a->external_address, &b->external_address)) {
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (a->tls.method != b->tls.method
|
|
|
|
|
|
|
|
|| a->tls.ciphers_num != b->tls.ciphers_num
|
|
|
|
|
|
|
|
|| a->tls.proto != b->tls.proto
|
|
|
|
|
|
|
|
|| a->tls.verify_client != b->tls.verify_client
|
|
|
|
|
|
|
|
|| a->tls.verify_server != b->tls.verify_server
|
|
|
|
|
|
|
|
|| a->tls.require_client_cert != b->tls.require_client_cert) {
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (memcmp(a->ciphers, b->ciphers, sizeof(pj_ssl_cipher) * fmax(a->tls.ciphers_num, b->tls.ciphers_num))) {
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void states_cleanup(void *states)
|
|
|
|
static void states_cleanup(void *states)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (states) {
|
|
|
|
if (states) {
|
|
|
@ -364,6 +403,9 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
|
|
|
|
RAII_VAR(struct internal_state *, perm_state, NULL, ao2_cleanup);
|
|
|
|
RAII_VAR(struct internal_state *, perm_state, NULL, ao2_cleanup);
|
|
|
|
RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
|
|
|
|
RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
|
|
|
|
pj_status_t res = -1;
|
|
|
|
pj_status_t res = -1;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
#define BIND_TRIES 3
|
|
|
|
|
|
|
|
#define BIND_DELAY_US 100000
|
|
|
|
|
|
|
|
|
|
|
|
if (!states) {
|
|
|
|
if (!states) {
|
|
|
|
return -1;
|
|
|
|
return -1;
|
|
|
@ -376,32 +418,39 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
ao2_wrlock(states);
|
|
|
|
ao2_wrlock(states);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
temp_state = internal_state_alloc(transport);
|
|
|
|
|
|
|
|
if (!temp_state) {
|
|
|
|
|
|
|
|
ast_log(LOG_ERROR, "Transport '%s' failed to allocate memory\n", transport_id);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
perm_state = find_internal_state_by_transport(transport);
|
|
|
|
perm_state = find_internal_state_by_transport(transport);
|
|
|
|
if (perm_state) {
|
|
|
|
if (perm_state) {
|
|
|
|
ast_sorcery_diff(sorcery, perm_state->transport, transport, &changes);
|
|
|
|
ast_sorcery_diff(sorcery, perm_state->transport, transport, &changes);
|
|
|
|
if (changes) {
|
|
|
|
if (!changes && !has_state_changed(perm_state->state, temp_state->state)) {
|
|
|
|
|
|
|
|
/* In case someone is using the deprecated fields, reset them */
|
|
|
|
|
|
|
|
transport->state = perm_state->state;
|
|
|
|
|
|
|
|
copy_state_to_transport(transport);
|
|
|
|
|
|
|
|
ao2_replace(perm_state->transport, transport);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!transport->allow_reload) {
|
|
|
|
if (!perm_state->change_detected) {
|
|
|
|
if (!perm_state->change_detected) {
|
|
|
|
perm_state->change_detected = 1;
|
|
|
|
perm_state->change_detected = 1;
|
|
|
|
ast_log(LOG_WARNING, "Transport '%s' is not reloadable, maintaining previous values\n", transport_id);
|
|
|
|
ast_log(LOG_WARNING, "Transport '%s' is not reloadable, maintaining previous values\n", transport_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* In case someone is using the deprecated fields, reset them */
|
|
|
|
/* In case someone is using the deprecated fields, reset them */
|
|
|
|
transport->state = perm_state->state;
|
|
|
|
transport->state = perm_state->state;
|
|
|
|
copy_state_to_transport(transport);
|
|
|
|
copy_state_to_transport(transport);
|
|
|
|
ao2_replace(perm_state->transport, transport);
|
|
|
|
ao2_replace(perm_state->transport, transport);
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
temp_state = internal_state_alloc(transport);
|
|
|
|
|
|
|
|
if (!temp_state) {
|
|
|
|
|
|
|
|
ast_log(LOG_ERROR, "Transport '%s' failed to allocate memory\n", transport_id);
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (temp_state->state->host.addr.sa_family != PJ_AF_INET && temp_state->state->host.addr.sa_family != PJ_AF_INET6) {
|
|
|
|
if (temp_state->state->host.addr.sa_family != PJ_AF_INET && temp_state->state->host.addr.sa_family != PJ_AF_INET6) {
|
|
|
|
ast_log(LOG_ERROR, "Transport '%s' could not be started as binding not specified\n", transport_id);
|
|
|
|
ast_log(LOG_ERROR, "Transport '%s' could not be started as binding not specified\n", transport_id);
|
|
|
|
goto error;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Set default port if not present */
|
|
|
|
/* Set default port if not present */
|
|
|
@ -418,20 +467,33 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external signaling address\n",
|
|
|
|
ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external signaling address\n",
|
|
|
|
transport_id);
|
|
|
|
transport_id);
|
|
|
|
goto error;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (ast_dnsmgr_lookup(transport->external_signaling_address, &temp_state->state->external_address, &temp_state->state->external_address_refresher, NULL) < 0) {
|
|
|
|
if (ast_dnsmgr_lookup(transport->external_signaling_address, &temp_state->state->external_address, &temp_state->state->external_address_refresher, NULL) < 0) {
|
|
|
|
ast_log(LOG_ERROR, "Could not create dnsmgr for external signaling address on '%s'\n", transport_id);
|
|
|
|
ast_log(LOG_ERROR, "Could not create dnsmgr for external signaling address on '%s'\n", transport_id);
|
|
|
|
goto error;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (transport->type == AST_TRANSPORT_UDP) {
|
|
|
|
if (transport->type == AST_TRANSPORT_UDP) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
|
|
|
|
|
|
|
|
if (perm_state && perm_state->state && perm_state->state->transport) {
|
|
|
|
|
|
|
|
pjsip_udp_transport_pause(perm_state->state->transport,
|
|
|
|
|
|
|
|
PJSIP_UDP_TRANSPORT_DESTROY_SOCKET);
|
|
|
|
|
|
|
|
usleep(BIND_DELAY_US);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
|
|
|
|
if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
|
|
|
|
res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(), &temp_state->state->host.ipv4, NULL, transport->async_operations, &temp_state->state->transport);
|
|
|
|
res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(),
|
|
|
|
|
|
|
|
&temp_state->state->host.ipv4, NULL, transport->async_operations,
|
|
|
|
|
|
|
|
&temp_state->state->transport);
|
|
|
|
} else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) {
|
|
|
|
} else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) {
|
|
|
|
res = pjsip_udp_transport_start6(ast_sip_get_pjsip_endpoint(), &temp_state->state->host.ipv6, NULL, transport->async_operations, &temp_state->state->transport);
|
|
|
|
res = pjsip_udp_transport_start6(ast_sip_get_pjsip_endpoint(),
|
|
|
|
|
|
|
|
&temp_state->state->host.ipv6, NULL, transport->async_operations,
|
|
|
|
|
|
|
|
&temp_state->state->transport);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (res == PJ_SUCCESS && (transport->tos || transport->cos)) {
|
|
|
|
if (res == PJ_SUCCESS && (transport->tos || transport->cos)) {
|
|
|
@ -451,18 +513,37 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
|
|
|
|
cfg.async_cnt = transport->async_operations;
|
|
|
|
cfg.async_cnt = transport->async_operations;
|
|
|
|
set_qos(transport, &cfg.qos_params);
|
|
|
|
set_qos(transport, &cfg.qos_params);
|
|
|
|
|
|
|
|
|
|
|
|
res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg, &temp_state->state->factory);
|
|
|
|
for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
|
|
|
|
|
|
|
|
if (perm_state && perm_state->state && perm_state->state->factory
|
|
|
|
|
|
|
|
&& perm_state->state->factory->destroy) {
|
|
|
|
|
|
|
|
perm_state->state->factory->destroy(perm_state->state->factory);
|
|
|
|
|
|
|
|
usleep(BIND_DELAY_US);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg,
|
|
|
|
|
|
|
|
&temp_state->state->factory);
|
|
|
|
|
|
|
|
}
|
|
|
|
} else if (transport->type == AST_TRANSPORT_TLS) {
|
|
|
|
} else if (transport->type == AST_TRANSPORT_TLS) {
|
|
|
|
if (transport->async_operations > 1 && ast_compare_versions(pj_get_version(), "2.5.0") < 0) {
|
|
|
|
if (transport->async_operations > 1 && ast_compare_versions(pj_get_version(), "2.5.0") < 0) {
|
|
|
|
ast_log(LOG_ERROR, "Transport: %s: When protocol=tls and pjproject version < 2.5.0, async_operations can't be > 1\n",
|
|
|
|
ast_log(LOG_ERROR, "Transport: %s: When protocol=tls and pjproject version < 2.5.0, async_operations can't be > 1\n",
|
|
|
|
ast_sorcery_object_get_id(obj));
|
|
|
|
ast_sorcery_object_get_id(obj));
|
|
|
|
goto error;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
temp_state->state->tls.password = pj_str((char*)transport->password);
|
|
|
|
temp_state->state->tls.password = pj_str((char*)transport->password);
|
|
|
|
set_qos(transport, &temp_state->state->tls.qos_params);
|
|
|
|
set_qos(transport, &temp_state->state->tls.qos_params);
|
|
|
|
|
|
|
|
|
|
|
|
res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &temp_state->state->tls, &temp_state->state->host, NULL, transport->async_operations, &temp_state->state->factory);
|
|
|
|
for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
|
|
|
|
|
|
|
|
if (perm_state && perm_state->state && perm_state->state->factory
|
|
|
|
|
|
|
|
&& perm_state->state->factory->destroy) {
|
|
|
|
|
|
|
|
perm_state->state->factory->destroy(perm_state->state->factory);
|
|
|
|
|
|
|
|
usleep(BIND_DELAY_US);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &temp_state->state->tls,
|
|
|
|
|
|
|
|
&temp_state->state->host, NULL, transport->async_operations,
|
|
|
|
|
|
|
|
&temp_state->state->factory);
|
|
|
|
|
|
|
|
}
|
|
|
|
} else if ((transport->type == AST_TRANSPORT_WS) || (transport->type == AST_TRANSPORT_WSS)) {
|
|
|
|
} else if ((transport->type == AST_TRANSPORT_WS) || (transport->type == AST_TRANSPORT_WSS)) {
|
|
|
|
if (transport->cos || transport->tos) {
|
|
|
|
if (transport->cos || transport->tos) {
|
|
|
|
ast_log(LOG_WARNING, "TOS and COS values ignored for websocket transport\n");
|
|
|
|
ast_log(LOG_WARNING, "TOS and COS values ignored for websocket transport\n");
|
|
|
@ -475,17 +556,16 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
|
|
|
|
|
|
|
|
|
|
|
|
pj_strerror(res, msg, sizeof(msg));
|
|
|
|
pj_strerror(res, msg, sizeof(msg));
|
|
|
|
ast_log(LOG_ERROR, "Transport '%s' could not be started: %s\n", ast_sorcery_object_get_id(obj), msg);
|
|
|
|
ast_log(LOG_ERROR, "Transport '%s' could not be started: %s\n", ast_sorcery_object_get_id(obj), msg);
|
|
|
|
goto error;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
copy_state_to_transport(transport);
|
|
|
|
copy_state_to_transport(transport);
|
|
|
|
ao2_link(states, temp_state);
|
|
|
|
if (perm_state) {
|
|
|
|
|
|
|
|
ao2_unlink_flags(states, perm_state, OBJ_NOLOCK);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ao2_link_flags(states, temp_state, OBJ_NOLOCK);
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
|
|
|
ao2_unlink(states, temp_state);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*! \brief Custom handler for type just makes sure the state is created */
|
|
|
|
/*! \brief Custom handler for type just makes sure the state is created */
|
|
|
@ -1209,6 +1289,7 @@ int ast_sip_initialize_sorcery_transport(void)
|
|
|
|
ast_sorcery_object_field_register_custom(sorcery, "transport", "tos", "0", transport_tos_handler, tos_to_str, NULL, 0, 0);
|
|
|
|
ast_sorcery_object_field_register_custom(sorcery, "transport", "tos", "0", transport_tos_handler, tos_to_str, NULL, 0, 0);
|
|
|
|
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", "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", "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));
|
|
|
|
|
|
|
|
|
|
|
|
internal_sip_register_endpoint_formatter(&endpoint_transport_formatter);
|
|
|
|
internal_sip_register_endpoint_formatter(&endpoint_transport_formatter);
|
|
|
|
|
|
|
|
|
|
|
|