static int chan_pjsip_hangup(struct ast_channel *ast)
{
struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);
- struct chan_pjsip_pvt *pvt = channel->pvt;
- int cause = hangup_cause2sip(ast_channel_hangupcause(channel->session->channel));
- struct hangup_data *h_data = hangup_data_alloc(cause, ast);
+ struct chan_pjsip_pvt *pvt;
+ int cause;
+ struct hangup_data *h_data;
+
+ if (!channel || !channel->session) {
+ return -1;
+ }
+
+ pvt = channel->pvt;
+ cause = hangup_cause2sip(ast_channel_hangupcause(channel->session->channel));
+ h_data = hangup_data_alloc(cause, ast);
if (!h_data) {
goto failure;
struct ast_channel *chan;
chan = stuff;
+ ast_channel_hangupcause_set(chan, AST_CAUSE_NORMAL_CLEARING);
if (ast_pickup_call(chan)) {
ast_channel_hangupcause_set(chan, AST_CAUSE_CALL_REJECTED);
- } else {
- ast_channel_hangupcause_set(chan, AST_CAUSE_NORMAL_CLEARING);
}
ast_hangup(chan);
ast_channel_unref(chan);
--- /dev/null
+"""add default_from_user
+
+Revision ID: 154177371065
+Revises: 26f10cadc157
+Create Date: 2015-09-04 14:13:59.195013
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '154177371065'
+down_revision = '26f10cadc157'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ op.add_column('ps_globals', sa.Column('default_from_user', sa.String(80)))
+
+
+def downgrade():
+ op.drop_column('ps_globals', 'default_from_user')
*/
char *ast_sip_get_endpoint_identifier_order(void);
+/*!
+ * \brief Retrieve the global default from user.
+ *
+ * This is the value placed in outbound requests' From header if there
+ * is no better option (such as an endpoint-configured from_user or
+ * caller ID number).
+ *
+ * \retval The global default_from_user value.
+ */
+const char *ast_sip_get_default_from_user(void);
+
/*! \brief Determines whether the res_pjsip module is loaded */
#define CHECK_PJSIP_MODULE_LOADED() \
do { \
*/
int ast_sip_failover_request(pjsip_tx_data *tdata);
+/*
+ * \brief Retrieve the local host address in IP form
+ *
+ * \param af The address family to retrieve
+ * \param addr A place to store the local host address
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \since 13.6.0
+ */
+int ast_sip_get_host_ip(int af, pj_sockaddr *addr);
+
+/*!
+ * \brief Retrieve the local host address in string form
+ *
+ * \param af The address family to retrieve
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ *
+ * \since 13.6.0
+ *
+ * \note An empty string may be returned if the address family is valid but no local address exists
+ */
+const char *ast_sip_get_host_ip_string(int af);
+
#endif /* _RES_PJSIP_H */
int (*apply_negotiated_sdp_stream)(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream,
const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream);
/*!
+ * \brief Stop a session_media created by this handler but do not destroy resources
+ * \param session The session for which media is being stopped
+ * \param session_media The media to destroy
+ */
+ void (*stream_stop)(struct ast_sip_session_media *session_media);
+ /*!
* \brief Destroy a session_media created by this handler
* \param session The session for which media is being destroyed
* \param session_media The media to destroy
continue;
}
watchers = ao2_container_count(hint->callbacks);
- sprintf(buf, "%s@%s",
+ snprintf(buf, sizeof(buf), "%s@%s",
ast_get_extension_name(hint->exten),
ast_get_context_name(ast_get_extension_context(hint->exten)));
/* Wait for executing task to complete so that caller of ast_sched_del() does not
* free memory out from under the task.
*/
- ast_cond_wait(&s->cond, &con->lock);
+ while (con->currently_executing && (id == con->currently_executing->id)) {
+ ast_cond_wait(&s->cond, &con->lock);
+ }
/* Do not sched_release() here because ast_sched_runq() will do it */
}
return NULL;
}
+ /* To prevent another thread from finding and getting a reference to this
+ * taskprocessor we hold the singletons lock. If we didn't do this then
+ * they may acquire it and find that the listener has been shut down.
+ */
+ ao2_lock(tps_singletons);
+
if (ao2_ref(tps, -1) > 3) {
+ ao2_unlock(tps_singletons);
return NULL;
}
+
/* If we're down to 3 references, then those must be:
* 1. The reference we just got rid of
* 2. The container
* 3. The listener
*/
- ao2_unlink(tps_singletons, tps);
+ ao2_unlink_flags(tps_singletons, tps, OBJ_NOLOCK);
+ ao2_unlock(tps_singletons);
+
listener_shutdown(tps->listener);
return NULL;
}
Identifier names are usually derived from and can be found in the endpoint
identifier module itself (res_pjsip_endpoint_identifier_*)</synopsis>
</configOption>
+ <configOption name="default_from_user" default="asterisk">
+ <synopsis>When Asterisk generates an outgoing SIP request, the From header username will be
+ set to this value if there is no better option (such as CallerID) to be
+ used.</synopsis>
+ </configOption>
</configObject>
</configFile>
</configInfo>
static struct ast_threadpool *sip_threadpool;
+/*! Local host address for IPv4 */
+static pj_sockaddr host_ip_ipv4;
+
+/*! Local host address for IPv4 (string form) */
+static char host_ip_ipv4_string[PJ_INET6_ADDRSTRLEN + 2];
+
+/*! Local host address for IPv6 */
+static pj_sockaddr host_ip_ipv6;
+
+/*! Local host address for IPv6 (string form) */
+static char host_ip_ipv6_string[PJ_INET6_ADDRSTRLEN + 2];
+
static int register_service_noref(void *data)
{
pjsip_module **module = data;
pjsip_sip_uri *sip_uri;
pjsip_transport_type_e type = PJSIP_TRANSPORT_UNSPECIFIED;
int local_port;
- char uuid_str[AST_UUID_STR_LEN];
if (ast_strlen_zero(user)) {
- user = ast_uuid_generate_str(uuid_str, sizeof(uuid_str));
+ user = ast_sip_get_default_from_user();
}
/* Parse the provided target URI so we can determine what transport it will end up using */
pjsip_transaction *tsx;
if (pjsip_tsx_create_uas(NULL, rdata, &tsx) != PJ_SUCCESS) {
+ struct ast_sip_contact *contact;
+
+ /* ast_sip_create_response bumps the refcount of the contact and adds it to the tdata.
+ * We'll leak that reference if we don't get rid of it here.
+ */
+ contact = ast_sip_mod_data_get(tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT);
+ ao2_cleanup(contact);
+ ast_sip_mod_data_set(tdata->pool, tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT, NULL);
pjsip_tx_data_dec_ref(tdata);
return -1;
}
return res;
}
+int ast_sip_get_host_ip(int af, pj_sockaddr *addr)
+{
+ if (af == pj_AF_INET() && !ast_strlen_zero(host_ip_ipv4_string)) {
+ pj_sockaddr_copy_addr(addr, &host_ip_ipv4);
+ return 0;
+ } else if (af == pj_AF_INET6() && !ast_strlen_zero(host_ip_ipv6_string)) {
+ pj_sockaddr_copy_addr(addr, &host_ip_ipv6);
+ return 0;
+ }
+
+ return -1;
+}
+
+const char *ast_sip_get_host_ip_string(int af)
+{
+ if (af == pj_AF_INET()) {
+ return host_ip_ipv4_string;
+ } else if (af == pj_AF_INET6()) {
+ return host_ip_ipv6_string;
+ }
+
+ return NULL;
+}
+
static void remove_request_headers(pjsip_endpoint *endpt)
{
const pjsip_hdr *request_headers = pjsip_endpt_get_request_headers(endpt);
return AST_MODULE_LOAD_DECLINE;
}
+ if (!pj_gethostip(pj_AF_INET(), &host_ip_ipv4)) {
+ pj_sockaddr_print(&host_ip_ipv4, host_ip_ipv4_string, sizeof(host_ip_ipv4_string), 2);
+ ast_verb(3, "Local IPv4 address determined to be: %s\n", host_ip_ipv4_string);
+ }
+
+ if (!pj_gethostip(pj_AF_INET6(), &host_ip_ipv6)) {
+ pj_sockaddr_print(&host_ip_ipv6, host_ip_ipv6_string, sizeof(host_ip_ipv6_string), 2);
+ ast_verb(3, "Local IPv6 address determined to be: %s\n", host_ip_ipv6_string);
+ }
+
if (ast_sip_initialize_system()) {
ast_log(LOG_ERROR, "Failed to initialize SIP 'system' configuration section. Aborting load\n");
pj_pool_release(memory_pool);
#define DEFAULT_DEBUG "no"
#define DEFAULT_ENDPOINT_IDENTIFIER_ORDER "ip,username,anonymous"
#define DEFAULT_MAX_INITIAL_QUALIFY_TIME 0
+#define DEFAULT_FROM_USER "asterisk"
static char default_useragent[256];
AST_STRING_FIELD(debug);
/*! Order by which endpoint identifiers are checked (comma separated list) */
AST_STRING_FIELD(endpoint_identifier_order);
+ /*! User name to place in From header if there is no better option */
+ AST_STRING_FIELD(default_from_user);
);
/* Value to put in Max-Forwards header */
unsigned int max_forwards;
return time;
}
+const char *ast_sip_get_default_from_user(void)
+{
+ const char *from_user;
+ struct global_config *cfg;
+
+ cfg = get_global_cfg();
+ if (!cfg) {
+ return DEFAULT_FROM_USER;
+ }
+
+ from_user = cfg->default_from_user;
+ ao2_ref(cfg, -1);
+
+ return from_user;
+}
+
/*!
* \internal
* \brief Observer to set default global object if none exist.
ast_sorcery_object_field_register(sorcery, "global", "max_initial_qualify_time",
__stringify(DEFAULT_MAX_INITIAL_QUALIFY_TIME),
OPT_UINT_T, 0, FLDSET(struct global_config, max_initial_qualify_time));
+ ast_sorcery_object_field_register(sorcery, "global", "default_from_user", DEFAULT_FROM_USER,
+ OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_from_user));
if (ast_sorcery_instance_observer_add(sorcery, &observer_callbacks_global)) {
return -1;
#include "asterisk/res_pjsip.h"
#include "asterisk/module.h"
-/*! \brief Local host address for IPv4 */
-static char host_ipv4[PJ_INET_ADDRSTRLEN + 2];
-
-/*! \brief Local host address for IPv6 */
-static char host_ipv6[PJ_INET6_ADDRSTRLEN + 2];
-
/*! \brief Helper function which returns a UDP transport bound to the given address and port */
static pjsip_transport *multihomed_get_udp_transport(pj_str_t *address, int port)
{
}
/* If the host address is used in the SDP replace it with the address of what this is going out on */
- if ((!pj_strcmp2(&sdp->conn->addr_type, "IP4") && !pj_strcmp2(&sdp->conn->addr, host_ipv4)) ||
- (!pj_strcmp2(&sdp->conn->addr_type, "IP6") && !pj_strcmp2(&sdp->conn->addr, host_ipv6))) {
+ 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())))) {
return 1;
}
static int load_module(void)
{
char hostname[MAXHOSTNAMELEN] = "";
- pj_sockaddr addr;
CHECK_PJSIP_MODULE_LOADED();
hostname);
}
- if (!pj_gethostip(pj_AF_INET(), &addr)) {
- pj_sockaddr_print(&addr, host_ipv4, sizeof(host_ipv4), 2);
- ast_verb(3, "Local IPv4 address determined to be: %s\n", host_ipv4);
- }
-
- if (!pj_gethostip(pj_AF_INET6(), &addr)) {
- pj_sockaddr_print(&addr, host_ipv6, sizeof(host_ipv6), 2);
- ast_verb(3, "Local IPv6 address determined to be: %s\n", host_ipv6);
- }
-
if (ast_sip_register_service(&multihomed_module)) {
ast_log(LOG_ERROR, "Could not register multihomed module for incoming and outgoing requests\n");
return AST_MODULE_LOAD_FAILURE;
expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
sub_tree->persistence->expires = ast_tvadd(ast_tvnow(), ast_samp2tv(expires, 1));
- pjsip_msg_print(rdata->msg_info.msg, sub_tree->persistence->packet,
- sizeof(sub_tree->persistence->packet));
+ /* 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
+ * persistence that is pulled from persistent storage, though, the rdata->pkt_info.packet will
+ * only ever have a single SIP message on it, and so we base persistence on that.
+ */
+ if (rdata->msg_info.msg_buf) {
+ ast_copy_string(sub_tree->persistence->packet, rdata->msg_info.msg_buf,
+ MIN(sizeof(sub_tree->persistence->packet), rdata->msg_info.len));
+ } else {
+ ast_copy_string(sub_tree->persistence->packet, rdata->pkt_info.packet,
+ sizeof(sub_tree->persistence->packet));
+ }
ast_copy_string(sub_tree->persistence->src_name, rdata->pkt_info.src_name,
sizeof(sub_tree->persistence->src_name));
sub_tree->persistence->src_port = rdata->pkt_info.src_port;
*optimistic = 0;
+ if (!transport_str) {
+ return AST_SIP_MEDIA_TRANSPORT_INVALID;
+ }
if (strstr(transport_str, "UDP/TLS")) {
return AST_SIP_MEDIA_ENCRYPT_DTLS;
} else if (strstr(transport_str, "SAVP")) {
static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
static const pj_str_t STR_SENDONLY = { "sendonly", 8 };
pjmedia_sdp_media *media;
- char hostip[PJ_INET6_ADDRSTRLEN+2];
+ const char *hostip = NULL;
struct ast_sockaddr addr;
char tmp[512];
pj_str_t stmp;
/* Add connection level details */
if (direct_media_enabled) {
- ast_copy_string(hostip, ast_sockaddr_stringify_fmt(&session_media->direct_media_addr, AST_SOCKADDR_STR_ADDR), sizeof(hostip));
+ hostip = ast_sockaddr_stringify_fmt(&session_media->direct_media_addr, AST_SOCKADDR_STR_ADDR);
} else if (ast_strlen_zero(session->endpoint->media.address)) {
- pj_sockaddr localaddr;
-
- if (pj_gethostip(session->endpoint->media.rtp.ipv6 ? pj_AF_INET6() : pj_AF_INET(), &localaddr)) {
- return -1;
- }
- pj_sockaddr_print(&localaddr, hostip, sizeof(hostip), 2);
+ hostip = ast_sip_get_host_ip_string(session->endpoint->media.rtp.ipv6 ? pj_AF_INET6() : pj_AF_INET());
} else {
- ast_copy_string(hostip, session->endpoint->media.address, sizeof(hostip));
+ hostip = session->endpoint->media.address;
+ }
+
+ if (ast_strlen_zero(hostip)) {
+ ast_log(LOG_ERROR, "No local host IP available for stream %s\n", session_media->stream_type);
+ return -1;
}
media->conn->net_type = STR_IN;
* a NAT. This way there won't be an awkward delay before media starts flowing in some
* scenarios.
*/
+ AST_SCHED_DEL(sched, session_media->keepalive_sched_id);
session_media->keepalive_sched_id = ast_sched_add_variable(sched, 500, send_keepalive,
session_media, 1);
}
pj_strdup2(tdata->pool, &stream->conn->addr, transport->external_media_address);
}
+/*! \brief Function which stops the RTP instance */
+static void stream_stop(struct ast_sip_session_media *session_media)
+{
+ if (!session_media->rtp) {
+ return;
+ }
+
+ AST_SCHED_DEL(sched, session_media->keepalive_sched_id);
+ AST_SCHED_DEL(sched, session_media->timeout_sched_id);
+ ast_rtp_instance_stop(session_media->rtp);
+}
+
/*! \brief Function which destroys the RTP instance when session ends */
static void stream_destroy(struct ast_sip_session_media *session_media)
{
if (session_media->rtp) {
- AST_SCHED_DEL(sched, session_media->keepalive_sched_id);
- AST_SCHED_DEL(sched, session_media->timeout_sched_id);
- ast_rtp_instance_stop(session_media->rtp);
+ stream_stop(session_media);
ast_rtp_instance_destroy(session_media->rtp);
}
session_media->rtp = NULL;
.create_outgoing_sdp_stream = create_outgoing_sdp_stream,
.apply_negotiated_sdp_stream = apply_negotiated_sdp_stream,
.change_outgoing_sdp_stream_media_address = change_outgoing_sdp_stream_media_address,
+ .stream_stop = stream_stop,
.stream_destroy = stream_destroy,
};
.create_outgoing_sdp_stream = create_outgoing_sdp_stream,
.apply_negotiated_sdp_stream = apply_negotiated_sdp_stream,
.change_outgoing_sdp_stream_media_address = change_outgoing_sdp_stream_media_address,
+ .stream_stop = stream_stop,
.stream_destroy = stream_destroy,
};
}
}
}
+
+ if (session_media->handler && session_media->handler->stream_stop) {
+ ast_debug(1, "Stopping SDP media stream '%s' as it is not currently negotiated\n",
+ session_media->stream_type);
+ session_media->handler->stream_stop(session_media);
+ }
+
return CMP_MATCH;
}
break;
case PJSIP_EVENT_RX_MSG:
cb = ast_sip_mod_data_get(tsx->mod_data, session_module.id, MOD_DATA_ON_RESPONSE);
- handle_incoming(session, e->body.tsx_state.src.rdata, e->type,
- AST_SIP_SESSION_AFTER_MEDIA);
+ /* As the PJSIP invite session implementation responds with a 200 OK before we have a
+ * chance to be invoked session supplements for BYE requests actually end up executing
+ * in the invite session state callback as well. To prevent session supplements from
+ * running on the BYE request again we explicitly squash invocation of them here.
+ */
+ if ((e->body.tsx_state.src.rdata->msg_info.msg->type != PJSIP_REQUEST_MSG) ||
+ (tsx->method.id != PJSIP_BYE_METHOD)) {
+ handle_incoming(session, e->body.tsx_state.src.rdata, e->type,
+ AST_SIP_SESSION_AFTER_MEDIA);
+ }
if (tsx->method.id == PJSIP_INVITE_METHOD) {
if (tsx->role == PJSIP_ROLE_UAC) {
if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
if (!ast_strlen_zero(session->endpoint->media.address)) {
pj_strdup2(inv->pool_prov, &local->origin.addr, session->endpoint->media.address);
} else {
- pj_sockaddr localaddr;
- char our_ip[PJ_INET6_ADDRSTRLEN];
-
- pj_gethostip(session->endpoint->media.rtp.ipv6 ? pj_AF_INET6() : pj_AF_INET(), &localaddr);
- pj_sockaddr_print(&localaddr, our_ip, sizeof(our_ip), 0);
- pj_strdup2(inv->pool_prov, &local->origin.addr, our_ip);
+ pj_strdup2(inv->pool_prov, &local->origin.addr, ast_sip_get_host_ip_string(session->endpoint->media.rtp.ipv6 ? pj_AF_INET6() : pj_AF_INET()));
}
}
static const pj_str_t STR_T38UDPREDUNDANCY = { "t38UDPRedundancy", 16 };
struct t38_state *state;
pjmedia_sdp_media *media;
- char hostip[PJ_INET6_ADDRSTRLEN+2];
+ const char *hostip = NULL;
struct ast_sockaddr addr;
char tmp[512];
pj_str_t stmp;
media->desc.transport = STR_UDPTL;
if (ast_strlen_zero(session->endpoint->media.address)) {
- pj_sockaddr localaddr;
-
- if (pj_gethostip(session->endpoint->media.t38.ipv6 ? pj_AF_INET6() : pj_AF_INET(), &localaddr)) {
- return -1;
- }
- pj_sockaddr_print(&localaddr, hostip, sizeof(hostip), 2);
+ hostip = ast_sip_get_host_ip_string(session->endpoint->media.t38.ipv6 ? pj_AF_INET6() : pj_AF_INET());
} else {
- ast_copy_string(hostip, session->endpoint->media.address, sizeof(hostip));
+ hostip = session->endpoint->media.address;
+ }
+
+ if (ast_strlen_zero(hostip)) {
+ return -1;
}
media->conn->net_type = STR_IN;
CHECK_PJSIP_MODULE_LOADED();
pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE, "WS", 5060, &transport_type_ws);
- pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE, "WSS", 5060, &transport_type_wss);
+ pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE, "WSS", 5060, &transport_type_wss);
if (ast_sip_register_service(&websocket_module) != PJ_SUCCESS) {
return AST_MODULE_LOAD_DECLINE;
ao2_iterator_destroy(&i);
if (has_rtp && has_rtcp &&
- pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, ao2_container_count(
- rtp->ice_active_remote_candidates), &candidates[0]) == PJ_SUCCESS) {
+ pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, cand_cnt, &candidates[0]) == PJ_SUCCESS) {
ast_test_suite_event_notify("ICECHECKLISTCREATE", "Result: SUCCESS");
pj_ice_sess_start_check(rtp->ice);
pj_timer_heap_poll(timer_heap, NULL);