Skip to content
Snippets Groups Projects
chan_pjsip.c 72.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Asterisk -- An open source telephony toolkit.
     *
     * Copyright (C) 2013, Digium, Inc.
     *
     * Joshua Colp <jcolp@digium.com>
     *
     * See http://www.asterisk.org for more information about
     * the Asterisk project. Please do not directly contact
     * any of the maintainers of this project for assistance;
     * the project provides a web site, mailing lists and IRC
     * channels for your use.
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License Version 2. See the LICENSE file
     * at the top of the source tree.
     */
    
    /*! \file
     *
     * \author Joshua Colp <jcolp@digium.com>
     *
    
     * \brief PSJIP SIP Channel Driver
    
     *
     * \ingroup channel_drivers
     */
    
    /*** MODULEINFO
    	<depend>pjproject</depend>
    
    	<depend>res_pjsip</depend>
    	<depend>res_pjsip_session</depend>
    
    	<support_level>core</support_level>
     ***/
    
    #include "asterisk.h"
    
    #include <pjsip.h>
    #include <pjsip_ua.h>
    #include <pjlib.h>
    
    
    ASTERISK_REGISTER_FILE()
    
    
    #include "asterisk/lock.h"
    #include "asterisk/channel.h"
    #include "asterisk/module.h"
    #include "asterisk/pbx.h"
    #include "asterisk/rtp_engine.h"
    #include "asterisk/acl.h"
    #include "asterisk/callerid.h"
    #include "asterisk/file.h"
    #include "asterisk/cli.h"
    #include "asterisk/app.h"
    #include "asterisk/musiconhold.h"
    #include "asterisk/causes.h"
    #include "asterisk/taskprocessor.h"
    
    #include "asterisk/dsp.h"
    
    #include "asterisk/stasis_endpoints.h"
    #include "asterisk/stasis_channels.h"
    
    #include "asterisk/indications.h"
    
    #include "asterisk/threadstorage.h"
    
    #include "asterisk/features_config.h"
    #include "asterisk/pickup.h"
    
    #include "asterisk/test.h"
    
    #include "asterisk/res_pjsip.h"
    #include "asterisk/res_pjsip_session.h"
    
    #include "pjsip/include/chan_pjsip.h"
    #include "pjsip/include/dialplan_functions.h"
    
    AST_THREADSTORAGE(uniqueid_threadbuf);
    #define UNIQUEID_BUFSIZE 256
    
    
    static const char channel_type[] = "PJSIP";
    
    static void chan_pjsip_pvt_dtor(void *obj)
    
    	struct chan_pjsip_pvt *pvt = obj;
    
    	for (i = 0; i < SIP_MEDIA_SIZE; ++i) {
    		ao2_cleanup(pvt->media[i]);
    		pvt->media[i] = NULL;
    	}
    }
    
    /* \brief Asterisk core interaction functions */
    
    static struct ast_channel *chan_pjsip_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause);
    
    static int chan_pjsip_sendtext(struct ast_channel *ast, const char *text);
    static int chan_pjsip_digit_begin(struct ast_channel *ast, char digit);
    static int chan_pjsip_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
    static int chan_pjsip_call(struct ast_channel *ast, const char *dest, int timeout);
    static int chan_pjsip_hangup(struct ast_channel *ast);
    static int chan_pjsip_answer(struct ast_channel *ast);
    static struct ast_frame *chan_pjsip_read(struct ast_channel *ast);
    static int chan_pjsip_write(struct ast_channel *ast, struct ast_frame *f);
    static int chan_pjsip_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
    static int chan_pjsip_transfer(struct ast_channel *ast, const char *target);
    static int chan_pjsip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
    static int chan_pjsip_devicestate(const char *data);
    static int chan_pjsip_queryoption(struct ast_channel *ast, int option, void *data, int *datalen);
    
    static const char *chan_pjsip_get_uniqueid(struct ast_channel *ast);
    
    
    /*! \brief PBX interface structure for channel registration */
    
    struct ast_channel_tech chan_pjsip_tech = {
    
    	.type = channel_type,
    
    	.description = "PJSIP Channel Driver",
    	.requester = chan_pjsip_request,
    	.send_text = chan_pjsip_sendtext,
    	.send_digit_begin = chan_pjsip_digit_begin,
    	.send_digit_end = chan_pjsip_digit_end,
    	.call = chan_pjsip_call,
    	.hangup = chan_pjsip_hangup,
    	.answer = chan_pjsip_answer,
    	.read = chan_pjsip_read,
    	.write = chan_pjsip_write,
    	.write_video = chan_pjsip_write,
    	.exception = chan_pjsip_read,
    	.indicate = chan_pjsip_indicate,
    	.transfer = chan_pjsip_transfer,
    	.fixup = chan_pjsip_fixup,
    	.devicestate = chan_pjsip_devicestate,
    	.queryoption = chan_pjsip_queryoption,
    
    	.func_channel_read = pjsip_acf_channel_read,
    
    	.get_pvt_uniqueid = chan_pjsip_get_uniqueid,
    
    	.properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER
    };
    
    /*! \brief SIP session interaction functions */
    
    static void chan_pjsip_session_begin(struct ast_sip_session *session);
    static void chan_pjsip_session_end(struct ast_sip_session *session);
    static int chan_pjsip_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata);
    static void chan_pjsip_incoming_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata);
    
    
    /*! \brief SIP session supplement structure */
    
    static struct ast_sip_session_supplement chan_pjsip_supplement = {
    
    	.method = "INVITE",
    
    	.priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL,
    
    	.session_begin = chan_pjsip_session_begin,
    	.session_end = chan_pjsip_session_end,
    	.incoming_request = chan_pjsip_incoming_request,
    	.incoming_response = chan_pjsip_incoming_response,
    
    	/* It is important that this supplement runs after media has been negotiated */
    	.response_priority = AST_SIP_SESSION_AFTER_MEDIA,
    
    static int chan_pjsip_incoming_ack(struct ast_sip_session *session, struct pjsip_rx_data *rdata);
    
    static struct ast_sip_session_supplement chan_pjsip_ack_supplement = {
    
    	.method = "ACK",
    
    	.priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL,
    
    	.incoming_request = chan_pjsip_incoming_ack,
    
    };
    
    /*! \brief Function called by RTP engine to get local audio RTP peer */
    
    static enum ast_rtp_glue_result chan_pjsip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
    
    	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan);
    
    	struct chan_pjsip_pvt *pvt = channel->pvt;
    
    	struct ast_sip_endpoint *endpoint;
    
    
    	if (!pvt || !channel->session || !pvt->media[SIP_MEDIA_AUDIO]->rtp) {
    
    		return AST_RTP_GLUE_RESULT_FORBID;
    	}
    
    
    	endpoint = channel->session->endpoint;
    
    
    	*instance = pvt->media[SIP_MEDIA_AUDIO]->rtp;
    	ao2_ref(*instance, +1);
    
    	ast_assert(endpoint != NULL);
    
    	if (endpoint->media.rtp.encryption != AST_SIP_MEDIA_ENCRYPT_NONE) {
    
    		return AST_RTP_GLUE_RESULT_FORBID;
    	}
    
    
    	if (endpoint->media.direct_media.enabled) {
    
    		return AST_RTP_GLUE_RESULT_REMOTE;
    	}
    
    	return AST_RTP_GLUE_RESULT_LOCAL;
    }
    
    /*! \brief Function called by RTP engine to get local video RTP peer */
    
    static enum ast_rtp_glue_result chan_pjsip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
    
    	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan);
    
    	struct chan_pjsip_pvt *pvt = channel->pvt;
    
    	struct ast_sip_endpoint *endpoint;
    
    	if (!pvt || !channel->session || !pvt->media[SIP_MEDIA_VIDEO]->rtp) {
    
    		return AST_RTP_GLUE_RESULT_FORBID;
    	}
    
    
    	endpoint = channel->session->endpoint;
    
    
    	*instance = pvt->media[SIP_MEDIA_VIDEO]->rtp;
    	ao2_ref(*instance, +1);
    
    
    	ast_assert(endpoint != NULL);
    
    	if (endpoint->media.rtp.encryption != AST_SIP_MEDIA_ENCRYPT_NONE) {
    
    		return AST_RTP_GLUE_RESULT_FORBID;
    	}
    
    
    	return AST_RTP_GLUE_RESULT_LOCAL;
    }
    
    /*! \brief Function called by RTP engine to get peer capabilities */
    
    static void chan_pjsip_get_codec(struct ast_channel *chan, struct ast_format_cap *result)
    
    	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan);
    
    	ast_format_cap_append_from_cap(result, channel->session->endpoint->media.codecs, AST_MEDIA_TYPE_UNKNOWN);
    
    }
    
    static int send_direct_media_request(void *data)
    {
    
    	struct ast_sip_session *session = data;
    	int res;
    
    	res = ast_sip_session_refresh(session, NULL, NULL, NULL,
    		session->endpoint->media.direct_media.method, 1);
    	ao2_ref(session, -1);
    	return res;
    
    /*! \brief Destructor function for \ref transport_info_data */
    static void transport_info_destroy(void *obj)
    {
    	struct transport_info_data *data = obj;
    	ast_free(data);
    }
    
    /*! \brief Datastore used to store local/remote addresses for the
     * INVITE request that created the PJSIP channel */
    static struct ast_datastore_info transport_info = {
    	.type = "chan_pjsip_transport_info",
    	.destroy = transport_info_destroy,
    };
    
    
    static struct ast_datastore_info direct_media_mitigation_info = { };
    
    static int direct_media_mitigate_glare(struct ast_sip_session *session)
    {
    	RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
    
    
    	if (session->endpoint->media.direct_media.glare_mitigation ==
    
    			AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_NONE) {
    		return 0;
    	}
    
    	datastore = ast_sip_session_get_datastore(session, "direct_media_glare_mitigation");
    	if (!datastore) {
    		return 0;
    	}
    
    	/* Removing the datastore ensures we won't try to mitigate glare on subsequent reinvites */
    	ast_sip_session_remove_datastore(session, "direct_media_glare_mitigation");
    
    
    	if ((session->endpoint->media.direct_media.glare_mitigation ==
    
    			AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_OUTGOING &&
    			session->inv_session->role == PJSIP_ROLE_UAC) ||
    
    			(session->endpoint->media.direct_media.glare_mitigation ==
    
    			AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_INCOMING &&
    			session->inv_session->role == PJSIP_ROLE_UAS)) {
    		return 1;
    	}
    
    	return 0;
    }
    
    static int check_for_rtp_changes(struct ast_channel *chan, struct ast_rtp_instance *rtp,
    		struct ast_sip_session_media *media, int rtcp_fd)
    {
    	int changed = 0;
    
    	if (rtp) {
    		changed = ast_rtp_instance_get_and_cmp_remote_address(rtp, &media->direct_media_addr);
    		if (media->rtp) {
    			ast_channel_set_fd(chan, rtcp_fd, -1);
    			ast_rtp_instance_set_prop(media->rtp, AST_RTP_PROPERTY_RTCP, 0);
    		}
    	} else if (!ast_sockaddr_isnull(&media->direct_media_addr)){
    		ast_sockaddr_setnull(&media->direct_media_addr);
    		changed = 1;
    		if (media->rtp) {
    			ast_rtp_instance_set_prop(media->rtp, AST_RTP_PROPERTY_RTCP, 1);
    			ast_channel_set_fd(chan, rtcp_fd, ast_rtp_instance_fd(media->rtp, 1));
    		}
    	}
    
    	return changed;
    }
    
    /*! \brief Function called by RTP engine to change where the remote party should send media */
    
    static int chan_pjsip_set_rtp_peer(struct ast_channel *chan,
    
    		struct ast_rtp_instance *rtp,
    		struct ast_rtp_instance *vrtp,
    		struct ast_rtp_instance *tpeer,
    		const struct ast_format_cap *cap,
    		int nat_active)
    {
    
    	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan);
    
    	struct chan_pjsip_pvt *pvt = channel->pvt;
    
    	struct ast_sip_session *session = channel->session;
    
    	int changed = 0;
    
    	/* Don't try to do any direct media shenanigans on early bridges */
    
    	if ((rtp || vrtp || tpeer) && !ast_channel_is_bridged(chan)) {
    
    		ast_debug(4, "Disregarding setting RTP on %s: channel is not bridged\n", ast_channel_name(chan));
    
    	if (nat_active && session->endpoint->media.direct_media.disable_on_nat) {
    
    		ast_debug(4, "Disregarding setting RTP on %s: NAT is active\n", ast_channel_name(chan));
    
    		return 0;
    	}
    
    	if (pvt->media[SIP_MEDIA_AUDIO]) {
    		changed |= check_for_rtp_changes(chan, rtp, pvt->media[SIP_MEDIA_AUDIO], 1);
    	}
    	if (pvt->media[SIP_MEDIA_VIDEO]) {
    		changed |= check_for_rtp_changes(chan, vrtp, pvt->media[SIP_MEDIA_VIDEO], 3);
    	}
    
    	if (direct_media_mitigate_glare(session)) {
    
    		ast_debug(4, "Disregarding setting RTP on %s: mitigating re-INVITE glare\n", ast_channel_name(chan));
    
    	if (cap && ast_format_cap_count(cap) && !ast_format_cap_identical(session->direct_media_cap, cap)) {
    		ast_format_cap_remove_by_type(session->direct_media_cap, AST_MEDIA_TYPE_UNKNOWN);
    		ast_format_cap_append_from_cap(session->direct_media_cap, cap, AST_MEDIA_TYPE_UNKNOWN);
    
    		changed = 1;
    	}
    
    	if (changed) {
    		ao2_ref(session, +1);
    
    		ast_debug(4, "RTP changed on %s; initiating direct media update\n", ast_channel_name(chan));
    
    		if (ast_sip_push_task(session->serializer, send_direct_media_request, session)) {
    			ao2_cleanup(session);
    		}
    
    	}
    
    	return 0;
    }
    
    /*! \brief Local glue for interacting with the RTP engine core */
    
    static struct ast_rtp_glue chan_pjsip_rtp_glue = {
    	.type = "PJSIP",
    	.get_rtp_info = chan_pjsip_get_rtp_peer,
    	.get_vrtp_info = chan_pjsip_get_vrtp_peer,
    	.get_codec = chan_pjsip_get_codec,
    	.update_peer = chan_pjsip_set_rtp_peer,
    
    static void set_channel_on_rtp_instance(struct chan_pjsip_pvt *pvt, const char *channel_id)
    {
    	if (pvt->media[SIP_MEDIA_AUDIO] && pvt->media[SIP_MEDIA_AUDIO]->rtp) {
    		ast_rtp_instance_set_channel_id(pvt->media[SIP_MEDIA_AUDIO]->rtp, channel_id);
    	}
    	if (pvt->media[SIP_MEDIA_VIDEO] && pvt->media[SIP_MEDIA_VIDEO]->rtp) {
    		ast_rtp_instance_set_channel_id(pvt->media[SIP_MEDIA_VIDEO]->rtp, channel_id);
    	}
    }
    
    
    /*! \brief Function called to create a new PJSIP Asterisk channel */
    
    static struct ast_channel *chan_pjsip_new(struct ast_sip_session *session, int state, const char *exten, const char *title, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *cid_name)
    
    {
    	struct ast_channel *chan;
    
    	RAII_VAR(struct chan_pjsip_pvt *, pvt, NULL, ao2_cleanup);
    
    	struct ast_sip_channel_pvt *channel;
    
    	struct ast_variable *var;
    
    	if (!(pvt = ao2_alloc(sizeof(*pvt), chan_pjsip_pvt_dtor))) {
    
    	caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
    	if (!caps) {
    		return NULL;
    	}
    
    	chan = ast_channel_alloc_with_endpoint(1, state,
    		S_COR(session->id.number.valid, session->id.number.str, ""),
    		S_COR(session->id.name.valid, session->id.name.str, ""),
    		session->endpoint->accountcode, "", "", assignedids, requestor, 0,
    		session->endpoint->persistent, "PJSIP/%s-%08x",
    		ast_sorcery_object_get_id(session->endpoint),
    		(unsigned) ast_atomic_fetchadd_int((int *) &chan_idx, +1));
    
    	ast_channel_tech_set(chan, &chan_pjsip_tech);
    
    	if (!(channel = ast_sip_channel_pvt_alloc(pvt, session))) {
    
    		ast_channel_unlock(chan);
    
    	ast_channel_stage_snapshot(chan);
    
    
    	ast_channel_tech_pvt_set(chan, channel);
    
    	if (!ast_format_cap_count(session->req_caps) ||
    		!ast_format_cap_iscompatible(session->req_caps, session->endpoint->media.codecs)) {
    		ast_format_cap_append_from_cap(caps, session->endpoint->media.codecs, AST_MEDIA_TYPE_UNKNOWN);
    
    		ast_format_cap_append_from_cap(caps, session->req_caps, AST_MEDIA_TYPE_UNKNOWN);
    
    	ast_channel_nativeformats_set(chan, caps);
    
    	if (!ast_format_cap_empty(caps)) {
    
    		fmt = ast_format_cap_get_best_by_type(caps, AST_MEDIA_TYPE_AUDIO);
    		if (!fmt) {
    			/* Since our capabilities aren't empty, this will succeed */
    			fmt = ast_format_cap_get_format(caps, 0);
    		}
    
    		ast_channel_set_writeformat(chan, fmt);
    		ast_channel_set_rawwriteformat(chan, fmt);
    		ast_channel_set_readformat(chan, fmt);
    		ast_channel_set_rawreadformat(chan, fmt);
    		ao2_ref(fmt, -1);
    	}
    
    
    
    	if (state == AST_STATE_RING) {
    		ast_channel_rings_set(chan, 1);
    	}
    
    	ast_channel_adsicpe_set(chan, AST_ADSI_UNAVAILABLE);
    
    
    	ast_party_id_copy(&ast_channel_caller(chan)->id, &session->id);
    	ast_party_id_copy(&ast_channel_caller(chan)->ani, &session->id);
    
    
    	ast_channel_context_set(chan, session->endpoint->context);
    	ast_channel_exten_set(chan, S_OR(exten, "s"));
    	ast_channel_priority_set(chan, 1);
    
    
    	ast_channel_callgroup_set(chan, session->endpoint->pickup.callgroup);
    	ast_channel_pickupgroup_set(chan, session->endpoint->pickup.pickupgroup);
    
    	ast_channel_named_callgroups_set(chan, session->endpoint->pickup.named_callgroups);
    	ast_channel_named_pickupgroups_set(chan, session->endpoint->pickup.named_pickupgroups);
    
    	if (!ast_strlen_zero(session->endpoint->language)) {
    		ast_channel_language_set(chan, session->endpoint->language);
    	}
    
    	if (!ast_strlen_zero(session->endpoint->zone)) {
    		struct ast_tone_zone *zone = ast_get_indication_zone(session->endpoint->zone);
    		if (!zone) {
    			ast_log(LOG_ERROR, "Unknown country code '%s' for tonezone. Check indications.conf for available country codes.\n", session->endpoint->zone);
    		}
    		ast_channel_zone_set(chan, zone);
    	}
    
    
    	for (var = session->endpoint->channel_vars; var; var = var->next) {
    		char buf[512];
    		pbx_builtin_setvar_helper(chan, var->name, ast_get_encoded_str(
    						  var->value, buf, sizeof(buf)));
    	}
    
    
    	ast_channel_stage_snapshot_done(chan);
    
    	ast_channel_unlock(chan);
    
    	/* If res_pjsip_session is ever updated to create/destroy ast_sip_session_media
    	 * during a call such as if multiple same-type stream support is introduced,
    	 * these will need to be recaptured as well */
    	pvt->media[SIP_MEDIA_AUDIO] = ao2_find(session->media, "audio", OBJ_KEY);
    	pvt->media[SIP_MEDIA_VIDEO] = ao2_find(session->media, "video", OBJ_KEY);
    
    	set_channel_on_rtp_instance(pvt, ast_channel_uniqueid(chan));
    
    	return chan;
    }
    
    static int answer(void *data)
    {
    
    	pj_status_t status = PJ_SUCCESS;
    
    	pjsip_tx_data *packet = NULL;
    
    	struct ast_sip_session *session = data;
    
    
    	if (session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
    		return 0;
    	}
    
    
    	pjsip_dlg_inc_lock(session->inv_session->dlg);
    	if (session->inv_session->invite_tsx) {
    		status = pjsip_inv_answer(session->inv_session, 200, NULL, NULL, &packet);
    
    	} else {
    		ast_log(LOG_ERROR,"Cannot answer '%s' because there is no associated SIP transaction\n",
    			ast_channel_name(session->channel));
    
    	}
    	pjsip_dlg_dec_lock(session->inv_session->dlg);
    
    	if (status == PJ_SUCCESS && packet) {
    
    		ast_sip_session_send_response(session, packet);
    	}
    
    	return (status == PJ_SUCCESS) ? 0 : -1;
    }
    
    
    /*! \brief Function called by core when we should answer a PJSIP session */
    static int chan_pjsip_answer(struct ast_channel *ast)
    
    	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);
    
    
    	if (ast_channel_state(ast) == AST_STATE_UP) {
    		return 0;
    	}
    
    	ast_setstate(ast, AST_STATE_UP);
    
    	/* the answer task needs to be pushed synchronously otherwise a race condition
    	   can occur between this thread and bridging (specifically when native bridging
    	   attempts to do direct media) */
    	ast_channel_unlock(ast);
    	if (ast_sip_push_task_synchronous(session->serializer, answer, session)) {
    
    		ast_log(LOG_WARNING, "Unable to push answer task to the threadpool. Cannot answer call\n");
    
    		ao2_ref(session, -1);
    		ast_channel_lock(ast);
    
    	ao2_ref(session, -1);
    	ast_channel_lock(ast);
    
    /*! \brief Internal helper function called when CNG tone is detected */
    
    static struct ast_frame *chan_pjsip_cng_tone_detected(struct ast_sip_session *session, struct ast_frame *f)
    
    {
    	const char *target_context;
    	int exists;
    
    	/* If we only needed this DSP for fax detection purposes we can just drop it now */
    
    	if (session->endpoint->dtmf == AST_SIP_DTMF_INBAND || session->endpoint->dtmf == AST_SIP_DTMF_AUTO) {
    
    		ast_dsp_set_features(session->dsp, DSP_FEATURE_DIGIT_DETECT);
    	} else {
    		ast_dsp_free(session->dsp);
    		session->dsp = NULL;
    	}
    
    	/* If already executing in the fax extension don't do anything */
    	if (!strcmp(ast_channel_exten(session->channel), "fax")) {
    		return f;
    	}
    
    	target_context = S_OR(ast_channel_macrocontext(session->channel), ast_channel_context(session->channel));
    
    	/* We need to unlock the channel here because ast_exists_extension has the
    	 * potential to start and stop an autoservice on the channel. Such action
    	 * is prone to deadlock if the channel is locked.
    	 */
    	ast_channel_unlock(session->channel);
    	exists = ast_exists_extension(session->channel, target_context, "fax", 1,
    		S_COR(ast_channel_caller(session->channel)->id.number.valid,
    			ast_channel_caller(session->channel)->id.number.str, NULL));
    	ast_channel_lock(session->channel);
    
    	if (exists) {
    		ast_verb(2, "Redirecting '%s' to fax extension due to CNG detection\n",
    			ast_channel_name(session->channel));
    		pbx_builtin_setvar_helper(session->channel, "FAXEXTEN", ast_channel_exten(session->channel));
    		if (ast_async_goto(session->channel, target_context, "fax", 1)) {
    			ast_log(LOG_ERROR, "Failed to async goto '%s' into fax extension in '%s'\n",
    				ast_channel_name(session->channel), target_context);
    		}
    		ast_frfree(f);
    		f = &ast_null_frame;
    	} else {
    		ast_log(LOG_NOTICE, "FAX CNG detected on '%s' but no fax extension in '%s'\n",
    			ast_channel_name(session->channel), target_context);
    	}
    
    	return f;
    }
    
    
    /*! \brief Function called by core to read any waiting frames */
    
    static struct ast_frame *chan_pjsip_read(struct ast_channel *ast)
    
    	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);
    
    	struct chan_pjsip_pvt *pvt = channel->pvt;
    
    	struct ast_frame *f;
    	struct ast_sip_session_media *media = NULL;
    	int rtcp = 0;
    	int fdno = ast_channel_fdno(ast);
    
    	switch (fdno) {
    	case 0:
    		media = pvt->media[SIP_MEDIA_AUDIO];
    		break;
    	case 1:
    		media = pvt->media[SIP_MEDIA_AUDIO];
    		rtcp = 1;
    		break;
    	case 2:
    		media = pvt->media[SIP_MEDIA_VIDEO];
    		break;
    	case 3:
    		media = pvt->media[SIP_MEDIA_VIDEO];
    		rtcp = 1;
    		break;
    	}
    
    	if (!media || !media->rtp) {
    		return &ast_null_frame;
    	}
    
    
    	if (!(f = ast_rtp_instance_read(media->rtp, rtcp))) {
    		return f;
    	}
    
    	if (f->frametype != AST_FRAME_VOICE) {
    		return f;
    	}
    
    	if (channel->session->dsp) {
    		f = ast_dsp_process(ast, channel->session->dsp, f);
    
    
    		if (f && (f->frametype == AST_FRAME_DTMF)) {
    
    			if (f->subclass.integer == 'f') {
    				ast_debug(3, "Fax CNG detected on %s\n", ast_channel_name(ast));
    
    				f = chan_pjsip_cng_tone_detected(channel->session, f);
    
    			} else {
    				ast_debug(3, "* Detected inband DTMF '%c' on '%s'\n", f->subclass.integer,
    					ast_channel_name(ast));
    			}
    
    		}
    	}
    
    	return f;
    }
    
    /*! \brief Function called by core to write frames */
    
    static int chan_pjsip_write(struct ast_channel *ast, struct ast_frame *frame)
    
    	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);
    
    	struct chan_pjsip_pvt *pvt = channel->pvt;
    
    	struct ast_sip_session_media *media;
    
    
    	switch (frame->frametype) {
    	case AST_FRAME_VOICE:
    		media = pvt->media[SIP_MEDIA_AUDIO];
    
    		if (!media) {
    			return 0;
    		}
    
    		if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
    
    			struct ast_str *cap_buf = ast_str_alloca(128);
    			struct ast_str *write_transpath = ast_str_alloca(256);
    			struct ast_str *read_transpath = ast_str_alloca(256);
    
    
    			ast_log(LOG_WARNING,
    
    				"Channel %s asked to send %s frame when native formats are %s (rd:%s->%s;%s wr:%s->%s;%s)\n",
    				ast_channel_name(ast),
    
    				ast_format_get_name(frame->subclass.format),
    				ast_format_cap_get_names(ast_channel_nativeformats(ast), &cap_buf),
    
    				ast_format_get_name(ast_channel_rawreadformat(ast)),
    
    				ast_format_get_name(ast_channel_readformat(ast)),
    
    				ast_translate_path_to_str(ast_channel_readtrans(ast), &read_transpath),
    				ast_format_get_name(ast_channel_writeformat(ast)),
    				ast_format_get_name(ast_channel_rawwriteformat(ast)),
    				ast_translate_path_to_str(ast_channel_writetrans(ast), &write_transpath));
    
    			return 0;
    		}
    		if (media->rtp) {
    			res = ast_rtp_instance_write(media->rtp, frame);
    		}
    		break;
    	case AST_FRAME_VIDEO:
    		if ((media = pvt->media[SIP_MEDIA_VIDEO]) && media->rtp) {
    			res = ast_rtp_instance_write(media->rtp, frame);
    		}
    		break;
    
    	case AST_FRAME_MODEM:
    		break;
    
    		ast_log(LOG_WARNING, "Can't send %u type frames with PJSIP\n", frame->frametype);
    
    		break;
    	}
    
    	return res;
    }
    
    /*! \brief Function called by core to change the underlying owner channel */
    
    static int chan_pjsip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
    
    	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(newchan);
    
    	struct chan_pjsip_pvt *pvt = channel->pvt;
    
    	if (channel->session->channel != oldchan) {
    
    	/*
    	 * The masquerade has suspended the channel's session
    	 * serializer so we can safely change it outside of
    	 * the serializer thread.
    	 */
    	channel->session->channel = newchan;
    
    	set_channel_on_rtp_instance(pvt, ast_channel_uniqueid(newchan));
    
    /*! AO2 hash function for on hold UIDs */
    static int uid_hold_hash_fn(const void *obj, const int flags)
    {
    	const char *key = obj;
    
    	switch (flags & OBJ_SEARCH_MASK) {
    	case OBJ_SEARCH_KEY:
    		break;
    	case OBJ_SEARCH_OBJECT:
    		break;
    	default:
    		/* Hash can only work on something with a full key. */
    		ast_assert(0);
    		return 0;
    	}
    	return ast_str_hash(key);
    }
    
    /*! AO2 sort function for on hold UIDs */
    static int uid_hold_sort_fn(const void *obj_left, const void *obj_right, const int flags)
    {
    	const char *left = obj_left;
    	const char *right = obj_right;
    	int cmp;
    
    	switch (flags & OBJ_SEARCH_MASK) {
    	case OBJ_SEARCH_OBJECT:
    	case OBJ_SEARCH_KEY:
    		cmp = strcmp(left, right);
    		break;
    	case OBJ_SEARCH_PARTIAL_KEY:
    		cmp = strncmp(left, right, strlen(right));
    		break;
    	default:
    		/* Sort can only work on something with a full or partial key. */
    		ast_assert(0);
    		cmp = 0;
    		break;
    	}
    	return cmp;
    }
    
    static struct ao2_container *pjsip_uids_onhold;
    
    /*!
     * \brief Add a channel ID to the list of PJSIP channels on hold
     *
     * \param chan_uid - Unique ID of the channel being put into the hold list
     *
     * \retval 0 Channel has been added to or was already in the hold list
     * \retval -1 Failed to add channel to the hold list
     */
    static int chan_pjsip_add_hold(const char *chan_uid)
    {
    	RAII_VAR(char *, hold_uid, NULL, ao2_cleanup);
    
    	hold_uid = ao2_find(pjsip_uids_onhold, chan_uid, OBJ_SEARCH_KEY);
    	if (hold_uid) {
    		/* Device is already on hold. Nothing to do. */
    		return 0;
    	}
    
    	/* Device wasn't in hold list already. Create a new one. */
    	hold_uid = ao2_alloc_options(strlen(chan_uid) + 1, NULL,
    		AO2_ALLOC_OPT_LOCK_NOLOCK);
    	if (!hold_uid) {
    		return -1;
    	}
    
    	ast_copy_string(hold_uid, chan_uid, strlen(chan_uid) + 1);
    
    	if (ao2_link(pjsip_uids_onhold, hold_uid) == 0) {
    		return -1;
    	}
    
    	return 0;
    }
    
    /*!
     * \brief Remove a channel ID from the list of PJSIP channels on hold
     *
     * \param chan_uid - Unique ID of the channel being taken out of the hold list
     */
    static void chan_pjsip_remove_hold(const char *chan_uid)
    {
    	ao2_find(pjsip_uids_onhold, chan_uid, OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA);
    }
    
    /*!
     * \brief Determine whether a channel ID is in the list of PJSIP channels on hold
     *
     * \param chan_uid - Channel being checked
     *
     * \retval 0 The channel is not in the hold list
     * \retval 1 The channel is in the hold list
     */
    static int chan_pjsip_get_hold(const char *chan_uid)
    {
    	RAII_VAR(char *, hold_uid, NULL, ao2_cleanup);
    
    	hold_uid = ao2_find(pjsip_uids_onhold, chan_uid, OBJ_SEARCH_KEY);
    	if (!hold_uid) {
    		return 0;
    	}
    
    	return 1;
    }
    
    
    /*! \brief Function called to get the device state of an endpoint */
    
    static int chan_pjsip_devicestate(const char *data)
    
    {
    	RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", data), ao2_cleanup);
    	enum ast_device_state state = AST_DEVICE_UNKNOWN;
    	RAII_VAR(struct ast_endpoint_snapshot *, endpoint_snapshot, NULL, ao2_cleanup);
    
    	RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup);
    
    	struct ast_devstate_aggregate aggregate;
    	int num, inuse = 0;
    
    	if (!endpoint) {
    		return AST_DEVICE_INVALID;
    	}
    
    	endpoint_snapshot = ast_endpoint_latest_snapshot(ast_endpoint_get_tech(endpoint->persistent),
    
    		ast_endpoint_get_resource(endpoint->persistent));
    
    	if (!endpoint_snapshot) {
    		return AST_DEVICE_INVALID;
    	}
    
    
    	if (endpoint_snapshot->state == AST_ENDPOINT_OFFLINE) {
    		state = AST_DEVICE_UNAVAILABLE;
    	} else if (endpoint_snapshot->state == AST_ENDPOINT_ONLINE) {
    		state = AST_DEVICE_NOT_INUSE;
    	}
    
    
    	if (!endpoint_snapshot->num_channels || !(cache = ast_channel_cache())) {
    
    	ao2_ref(cache, +1);
    
    
    	for (num = 0; num < endpoint_snapshot->num_channels; num++) {
    
    		RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
    
    		msg = stasis_cache_get(cache, ast_channel_snapshot_type(),
    			endpoint_snapshot->channel_ids[num]);
    
    
    		if (chan_pjsip_get_hold(snapshot->uniqueid)) {
    			ast_devstate_aggregate_add(&aggregate, AST_DEVICE_ONHOLD);
    		} else {
    			ast_devstate_aggregate_add(&aggregate, ast_state_chan2dev(snapshot->state));
    		}
    
    		if ((snapshot->state == AST_STATE_UP) || (snapshot->state == AST_STATE_RING) ||
    
    			(snapshot->state == AST_STATE_BUSY)) {
    			inuse++;
    		}
    	}
    
    	if (endpoint->devicestate_busy_at && (inuse == endpoint->devicestate_busy_at)) {
    		state = AST_DEVICE_BUSY;
    	} else if (ast_devstate_aggregate_result(&aggregate) != AST_DEVICE_INVALID) {
    		state = ast_devstate_aggregate_result(&aggregate);
    	}
    
    	return state;
    }
    
    
    /*! \brief Function called to query options on a channel */
    
    static int chan_pjsip_queryoption(struct ast_channel *ast, int option, void *data, int *datalen)
    
    {
    	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);
    	struct ast_sip_session *session = channel->session;
    	int res = -1;
    
    	enum ast_t38_state state = T38_STATE_UNAVAILABLE;
    
    
    	switch (option) {
    	case AST_OPTION_T38_STATE:
    
    		if (session->endpoint->media.t38.enabled) {
    
    			switch (session->t38state) {
    			case T38_LOCAL_REINVITE:
    			case T38_PEER_REINVITE:
    				state = T38_STATE_NEGOTIATING;
    				break;
    			case T38_ENABLED:
    				state = T38_STATE_NEGOTIATED;
    				break;
    			case T38_REJECTED:
    				state = T38_STATE_REJECTED;
    				break;
    			default:
    				state = T38_STATE_UNKNOWN;
    				break;
    			}
    		}
    
    		*((enum ast_t38_state *) data) = state;
    		res = 0;
    
    		break;
    	default:
    		break;
    	}
    
    	return res;
    }
    
    
    static const char *chan_pjsip_get_uniqueid(struct ast_channel *ast)
    {
    	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);
    
    	char *uniqueid = ast_threadstorage_get(&uniqueid_threadbuf, UNIQUEID_BUFSIZE);
    
    	ast_copy_pj_str(uniqueid, &channel->session->inv_session->dlg->call_id->id, UNIQUEID_BUFSIZE);
    
    	return uniqueid;
    
    struct indicate_data {
    	struct ast_sip_session *session;
    	int condition;
    	int response_code;
    	void *frame_data;
    	size_t datalen;
    };
    
    static void indicate_data_destroy(void *obj)
    {
    	struct indicate_data *ind_data = obj;
    
    	ast_free(ind_data->frame_data);
    	ao2_ref(ind_data->session, -1);
    }
    
    static struct indicate_data *indicate_data_alloc(struct ast_sip_session *session,
    		int condition, int response_code, const void *frame_data, size_t datalen)
    {
    	struct indicate_data *ind_data = ao2_alloc(sizeof(*ind_data), indicate_data_destroy);
    
    	if (!ind_data) {
    		return NULL;
    	}
    
    	ind_data->frame_data = ast_malloc(datalen);
    	if (!ind_data->frame_data) {
    		ao2_ref(ind_data, -1);
    		return NULL;
    	}
    
    	memcpy(ind_data->frame_data, frame_data, datalen);
    	ind_data->datalen = datalen;
    	ind_data->condition = condition;
    	ind_data->response_code = response_code;
    	ao2_ref(session, +1);
    	ind_data->session = session;
    
    	return ind_data;
    }
    
    static int indicate(void *data)
    {
    
    	pjsip_tx_data *packet = NULL;
    
    	struct indicate_data *ind_data = data;
    	struct ast_sip_session *session = ind_data->session;
    	int response_code = ind_data->response_code;