コード例 #1
0
ファイル: pjsip_distributor.c プロジェクト: scudella/asterisk
static pj_bool_t authenticate(pjsip_rx_data *rdata)
{
	RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
	int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD;

	ast_assert(endpoint != NULL);

	if (is_ack) {
		return PJ_FALSE;
	}

	if (ast_sip_requires_authentication(endpoint, rdata)) {
		pjsip_tx_data *tdata;
		struct unidentified_request *unid;

		pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, 401, NULL, &tdata);
		switch (ast_sip_check_authentication(endpoint, rdata, tdata)) {
		case AST_SIP_AUTHENTICATION_CHALLENGE:
			/* Send the 401 we created for them */
			ast_sip_report_auth_challenge_sent(endpoint, rdata, tdata);
			if (pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL) != PJ_SUCCESS) {
				pjsip_tx_data_dec_ref(tdata);
			}
			return PJ_TRUE;
		case AST_SIP_AUTHENTICATION_SUCCESS:
			/* See note in endpoint_lookup about not holding an unnecessary write lock */
			unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY);
			if (unid) {
				ao2_unlink(unidentified_requests, unid);
				ao2_ref(unid, -1);
			}
			ast_sip_report_auth_success(endpoint, rdata);
			break;
		case AST_SIP_AUTHENTICATION_FAILED:
			log_failed_request(rdata, "Failed to authenticate", 0, 0);
			ast_sip_report_auth_failed_challenge_response(endpoint, rdata);
			if (pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL) != PJ_SUCCESS) {
				pjsip_tx_data_dec_ref(tdata);
			}
			return PJ_TRUE;
		case AST_SIP_AUTHENTICATION_ERROR:
			log_failed_request(rdata, "Error to authenticate", 0, 0);
			ast_sip_report_auth_failed_challenge_response(endpoint, rdata);
			pjsip_tx_data_dec_ref(tdata);
			pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
			return PJ_TRUE;
		}
		pjsip_tx_data_dec_ref(tdata);
	} else if (endpoint == artificial_endpoint) {
		/* Uh. Oh.  The artificial endpoint couldn't challenge so block the request. */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
		return PJ_TRUE;
	}

	return PJ_FALSE;
}
コード例 #2
0
static int load_module(void)
{
	CHECK_PJSIP_SESSION_MODULE_LOADED();

	if (ast_sip_register_service(&messaging_module) != PJ_SUCCESS) {
		return AST_MODULE_LOAD_DECLINE;
	}

	if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(),
				       NULL, PJSIP_H_ALLOW, NULL, 1,
				       &pjsip_message_method.name) != PJ_SUCCESS) {

		ast_sip_unregister_service(&messaging_module);
		return AST_MODULE_LOAD_DECLINE;
	}

	if (ast_msg_tech_register(&msg_tech)) {
		ast_sip_unregister_service(&messaging_module);
		return AST_MODULE_LOAD_DECLINE;
	}

	message_serializer = ast_sip_create_serializer("pjsip/messaging");
	if (!message_serializer) {
		ast_sip_unregister_service(&messaging_module);
		ast_msg_tech_unregister(&msg_tech);
		return AST_MODULE_LOAD_DECLINE;
	}

	ast_sip_session_register_supplement(&messaging_supplement);
	return AST_MODULE_LOAD_SUCCESS;
}
コード例 #3
0
ファイル: res_hep_pjsip.c プロジェクト: alyosha1879/asterisk
static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
{
	char local_buf[256];
	char remote_buf[256];
	char *uuid;
	struct hepv3_capture_info *capture_info;
	pjsip_tpmgr_fla2_param prm;

	capture_info = hepv3_create_capture_info(&rdata->pkt_info.packet, rdata->pkt_info.len);
	if (!capture_info) {
		return PJ_SUCCESS;
	}

	if (!rdata->pkt_info.src_addr_len) {
		return PJ_SUCCESS;
	}
	pj_sockaddr_print(&rdata->pkt_info.src_addr, remote_buf, sizeof(remote_buf), 3);

	/* Attempt to determine what IP address we probably received this packet on */
	pjsip_tpmgr_fla2_param_default(&prm);
	prm.tp_type = rdata->tp_info.transport->key.type;
	pj_strset2(&prm.dst_host, rdata->pkt_info.src_name);
	prm.local_if = PJ_TRUE;

	/* If we can't get the local address use what we have already */
	if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), rdata->tp_info.pool, &prm) != PJ_SUCCESS) {
		pj_sockaddr_print(&rdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3);
	} else {
		if (prm.tp_type & PJSIP_TRANSPORT_IPV6) {
			snprintf(local_buf, sizeof(local_buf), "[%.*s]:%hu",
				(int)pj_strlen(&prm.ret_addr),
				pj_strbuf(&prm.ret_addr),
				prm.ret_port);
		} else {
			snprintf(local_buf, sizeof(local_buf), "%.*s:%hu",
				(int)pj_strlen(&prm.ret_addr),
				pj_strbuf(&prm.ret_addr),
				prm.ret_port);
		}
	}

	uuid = assign_uuid(&rdata->msg_info.cid->id, &rdata->msg_info.to->tag, &rdata->msg_info.from->tag);
	if (!uuid) {
		ao2_ref(capture_info, -1);
		return PJ_SUCCESS;
	}

	ast_sockaddr_parse(&capture_info->src_addr, remote_buf, PARSE_PORT_REQUIRE);
	ast_sockaddr_parse(&capture_info->dst_addr, local_buf, PARSE_PORT_REQUIRE);
	capture_info->capture_time.tv_sec = rdata->pkt_info.timestamp.sec;
	capture_info->capture_time.tv_usec = rdata->pkt_info.timestamp.msec * 1000;
	capture_info->capture_type = HEPV3_CAPTURE_TYPE_SIP;
	capture_info->uuid = uuid;
	capture_info->zipped = 0;

	hepv3_send_packet(capture_info);

	return PJ_FALSE;
}
コード例 #4
0
ファイル: pjsip_options.c プロジェクト: litnimax/asterisk
int ast_res_pjsip_init_options_handling(int reload)
{
	static const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };

	if (reload) {
		qualify_and_schedule_all();
		return 0;
	}

	sched_qualifies = ao2_t_container_alloc(QUALIFIED_BUCKETS,
		sched_qualifies_hash_fn, sched_qualifies_cmp_fn,
		"Create container for scheduled qualifies");
	if (!sched_qualifies) {
		return -1;
	}

	if (pjsip_endpt_register_module(ast_sip_get_pjsip_endpoint(), &options_module) != PJ_SUCCESS) {
		ao2_cleanup(sched_qualifies);
		sched_qualifies = NULL;
		return -1;
	}

	if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW,
		NULL, 1, &STR_OPTIONS) != PJ_SUCCESS) {
		pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);
		ao2_cleanup(sched_qualifies);
		sched_qualifies = NULL;
		return -1;
	}

	if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "aor", &observer_callbacks_options)) {
		pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);
		ao2_cleanup(sched_qualifies);
		sched_qualifies = NULL;
		return -1;
	}

	internal_sip_register_endpoint_formatter(&contact_status_formatter);
	ast_manager_register_xml("PJSIPQualify", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_sip_qualify);
	ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));

	update_all_unqualified_endpoints();
	qualify_and_schedule_all();

	return 0;
}
コード例 #5
0
static int ami_show_registrations(struct mansession *s, const struct message *m)
{
	int count = 0;
	struct ast_sip_ami ami = { .s = s, .m = m, .arg = &count, .action_id = astman_get_header(m, "ActionID"), };

	astman_send_listack(s, m, "Following are Events for each Inbound "
			    "registration", "start");

	ami_registrations_endpoints(&ami);

	astman_send_list_complete_start(s, m, "InboundRegistrationDetailComplete", count);
	astman_send_list_complete_end(s);
	return 0;
}

#define AMI_SHOW_REGISTRATIONS "PJSIPShowRegistrationsInbound"

static pjsip_module registrar_module = {
	.name = { "Registrar", 9 },
	.id = -1,
	.priority = PJSIP_MOD_PRIORITY_APPLICATION,
	.on_rx_request = registrar_on_rx_request,
};

static int load_module(void)
{
	const pj_str_t STR_REGISTER = { "REGISTER", 8 };

	CHECK_PJSIP_MODULE_LOADED();

	if (!(serializers = ao2_container_alloc(
		      SERIALIZER_BUCKETS, serializer_hash, serializer_cmp))) {
		return AST_MODULE_LOAD_DECLINE;
	}

	if (ast_sip_register_service(&registrar_module)) {
		return AST_MODULE_LOAD_DECLINE;
	}

	if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW, NULL, 1, &STR_REGISTER) != PJ_SUCCESS) {
		ast_sip_unregister_service(&registrar_module);
		return AST_MODULE_LOAD_DECLINE;
	}

	ast_manager_register_xml(AMI_SHOW_REGISTRATIONS, EVENT_FLAG_SYSTEM,
				 ami_show_registrations);

	return AST_MODULE_LOAD_SUCCESS;
}

static int unload_module(void)
{
	ast_manager_unregister(AMI_SHOW_REGISTRATIONS);
	ast_sip_unregister_service(&registrar_module);

	ao2_cleanup(serializers);
	return 0;
}
コード例 #6
0
ファイル: pjsip_options.c プロジェクト: cfhb/vlink-cti
void ast_res_pjsip_cleanup_options_handling(void)
{
	ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options));
	ast_manager_unregister("PJSIPQualify");
	internal_sip_unregister_endpoint_formatter(&contact_status_formatter);

	pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);
	ao2_cleanup(sched_qualifies);
	sched_qualifies = NULL;
}
コード例 #7
0
ファイル: location.c プロジェクト: lyx2014/Asterisk
/*! \brief Helper function which validates a permanent contact */
static int permanent_contact_validate(void *data)
{
	const char *value = data;
	pj_pool_t *pool;
	pj_str_t contact_uri;
	static const pj_str_t HCONTACT = { "Contact", 7 };

	pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Permanent Contact Validation", 256, 256);
	if (!pool) {
		return -1;
	}

	pj_strdup2_with_null(pool, &contact_uri, value);
	if (!pjsip_parse_hdr(pool, &HCONTACT, contact_uri.ptr, contact_uri.slen, NULL)) {
		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
		return -1;
	}

	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
	return 0;
}
コード例 #8
0
ファイル: pjsip_options.c プロジェクト: litnimax/asterisk
static pj_status_t send_options_response(pjsip_rx_data *rdata, int code)
{
	pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
	pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
	pjsip_transaction *trans = pjsip_rdata_get_tsx(rdata);
	pjsip_tx_data *tdata;
	const pjsip_hdr *hdr;
	pj_status_t status;

	/* Make the response object */
	status = ast_sip_create_response(rdata, code, NULL, &tdata);
	if (status != PJ_SUCCESS) {
		ast_log(LOG_ERROR, "Unable to create response (%d)\n", status);
		return status;
	}

	/* Add appropriate headers */
	if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ACCEPT, NULL))) {
		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
	}
	if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ALLOW, NULL))) {
		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
	}
	if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED, NULL))) {
		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
	}

	/*
	 * XXX TODO: pjsip doesn't care a lot about either of these headers -
	 * while it provides specific methods to create them, they are defined
	 * to be the standard string header creation. We never did add them
	 * in chan_sip, although RFC 3261 says they SHOULD. Hard coded here.
	 */
	ast_sip_add_header(tdata, "Accept-Encoding", DEFAULT_ENCODING);
	ast_sip_add_header(tdata, "Accept-Language", DEFAULT_LANGUAGE);

	if (dlg && trans) {
		status = pjsip_dlg_send_response(dlg, trans, tdata);
	} else {
		struct ast_sip_endpoint *endpoint;

		endpoint = ast_pjsip_rdata_get_endpoint(rdata);
		status = ast_sip_send_stateful_response(rdata, tdata, endpoint);
		ao2_cleanup(endpoint);
	}

	if (status != PJ_SUCCESS) {
		ast_log(LOG_ERROR, "Unable to send response (%d)\n", status);
	}

	return status;
}
static int digest_create_request_with_auth_from_old(const struct ast_sip_auth_vector *auths, pjsip_rx_data *challenge,
		pjsip_tx_data *old_request, pjsip_tx_data **new_request)
{
	pjsip_auth_clt_sess auth_sess;
	pjsip_cseq_hdr *cseq;

	if (pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(),
				old_request->pool, 0) != PJ_SUCCESS) {
		ast_log(LOG_WARNING, "Failed to initialize client authentication session\n");
		return -1;
	}

	if (set_outbound_authentication_credentials(&auth_sess, auths, challenge)) {
		ast_log(LOG_WARNING, "Failed to set authentication credentials\n");
		return -1;
	}

	switch (pjsip_auth_clt_reinit_req(&auth_sess, challenge,
				old_request, new_request)) {
	case PJ_SUCCESS:
		/* PJSIP creates a new transaction for new_request (meaning it creates a new
		 * branch). However, it recycles the Call-ID, from-tag, and CSeq from the
		 * original request. Some SIP implementations will not process the new request
		 * since the CSeq is the same as the original request. Incrementing it here
		 * fixes the interop issue
		 */
		cseq = pjsip_msg_find_hdr((*new_request)->msg, PJSIP_H_CSEQ, NULL);
		ast_assert(cseq != NULL);
		++cseq->cseq;
		return 0;
	case PJSIP_ENOCREDENTIAL:
		ast_log(LOG_WARNING, "Unable to create request with auth."
				"No auth credentials for any realms in challenge.\n");
		break;
	case PJSIP_EAUTHSTALECOUNT:
		ast_log(LOG_WARNING, "Unable to create request with auth."
				"Number of stale retries exceeded\n");
		break;
	case PJSIP_EFAILEDCREDENTIAL:
		ast_log(LOG_WARNING, "Authentication credentials not accepted by server\n");
		break;
	default:
		ast_log(LOG_WARNING, "Unable to create request with auth. Unknown failure\n");
		break;
	}

	return -1;
}
コード例 #10
0
static int load_module(void)
{
	const pj_str_t STR_REGISTER = { "REGISTER", 8 };

	if (!(serializers = ao2_container_alloc(
		      SERIALIZER_BUCKETS, serializer_hash, serializer_cmp))) {
		return AST_MODULE_LOAD_DECLINE;
	}

	if (ast_sip_register_service(&registrar_module)) {
		return AST_MODULE_LOAD_DECLINE;
	}

	if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW, NULL, 1, &STR_REGISTER) != PJ_SUCCESS) {
		ast_sip_unregister_service(&registrar_module);
		return AST_MODULE_LOAD_DECLINE;
	}

	return AST_MODULE_LOAD_SUCCESS;
}
コード例 #11
0
ファイル: res_pjsip_acl.c プロジェクト: GGGO/asterisk
static pj_bool_t acl_on_rx_msg(pjsip_rx_data *rdata)
{
	RAII_VAR(struct ao2_container *, acls, ast_sorcery_retrieve_by_fields(
			 ast_sip_get_sorcery(), SIP_SORCERY_ACL_TYPE,
			 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL), ao2_cleanup);
	RAII_VAR(struct ast_sip_acl *, matched_acl, NULL, ao2_cleanup);

	if (!acls) {
		ast_log(LOG_ERROR, "Unable to retrieve ACL sorcery data\n");
		return PJ_FALSE;
	}

	if ((matched_acl = ao2_callback(acls, 0, check_acls, rdata))) {
		if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
			pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
		}
		return PJ_TRUE;
	}

	return PJ_FALSE;
}
コード例 #12
0
static int options_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata)
{
    pjsip_tx_data *tdata;
    pj_status_t status;
    const pjsip_hdr *hdr;
    pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();

    status = pjsip_dlg_create_response(session->inv_session->dlg, rdata, 200, NULL,&tdata);
    if (status != PJ_SUCCESS) {
        ast_log(LOG_ERROR, "Unable to create response (%d)\n", status);
        return status;
    }

    /* Add appropriate headers */
    if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ACCEPT, NULL))) {
        pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
    }
    if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ALLOW, NULL))) {
        pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
    }
    if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED, NULL))) {
        pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
    }

    /*
     * XXX TODO: pjsip doesn't care a lot about either of these headers -
     * while it provides specific methods to create them, they are defined
     * to be the standard string header creation. We never did add them
     * in chan_sip, although RFC 3261 says they SHOULD. Hard coded here.
    */
    ast_sip_add_header(tdata, "Accept-Encoding", DEFAULT_ENCODING);
    ast_sip_add_header(tdata, "Accept-Language", DEFAULT_LANGUAGE);

    status = pjsip_dlg_send_response(session->inv_session->dlg, pjsip_rdata_get_tsx(rdata), tdata);
    if (status != PJ_SUCCESS) {
        ast_log(LOG_ERROR, "Unable to send response (%d)\n", status);
    }

    return status;
}
コード例 #13
0
static int load_module(void)
{
	if (ast_sip_register_service(&messaging_module) != PJ_SUCCESS) {
		return AST_MODULE_LOAD_DECLINE;
	}

	if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(),
				       NULL, PJSIP_H_ALLOW, NULL, 1,
				       &pjsip_message_method.name) != PJ_SUCCESS) {

		ast_sip_unregister_service(&messaging_module);
		return AST_MODULE_LOAD_DECLINE;
	}

	if (ast_msg_tech_register(&msg_tech)) {
		ast_sip_unregister_service(&messaging_module);
		return AST_MODULE_LOAD_DECLINE;
	}

	ast_sip_session_register_supplement(&messaging_supplement);
	return AST_MODULE_LOAD_SUCCESS;
}
コード例 #14
0
static int digest_create_request_with_auth_from_old(const struct ast_sip_auth_vector *auths, pjsip_rx_data *challenge,
		pjsip_tx_data *old_request, pjsip_tx_data **new_request)
{
	pjsip_auth_clt_sess auth_sess;

	if (pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(),
				old_request->pool, 0) != PJ_SUCCESS) {
		ast_log(LOG_WARNING, "Failed to initialize client authentication session\n");
		return -1;
	}

	if (set_outbound_authentication_credentials(&auth_sess, auths, challenge)) {
		ast_log(LOG_WARNING, "Failed to set authentication credentials\n");
		return -1;
	}

	switch (pjsip_auth_clt_reinit_req(&auth_sess, challenge,
				old_request, new_request)) {
	case PJ_SUCCESS:
		return 0;
	case PJSIP_ENOCREDENTIAL:
		ast_log(LOG_WARNING, "Unable to create request with auth."
				"No auth credentials for any realms in challenge.\n");
		break;
	case PJSIP_EAUTHSTALECOUNT:
		ast_log(LOG_WARNING, "Unable to create request with auth."
				"Number of stale retries exceeded\n");
		break;
	case PJSIP_EFAILEDCREDENTIAL:
		ast_log(LOG_WARNING, "Authentication credentials not accepted by server\n");
		break;
	default:
		ast_log(LOG_WARNING, "Unable to create request with auth. Unknown failure\n");
		break;
	}

	return -1;
}
コード例 #15
0
static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
{
	pjsip_tpmgr_fla2_param prm;
	pjsip_transport *transport = NULL;
	pjsip_cseq_hdr *cseq;
	pjsip_via_hdr *via;

	/* Use the destination information to determine what local interface this message will go out on */
	pjsip_tpmgr_fla2_param_default(&prm);
	prm.tp_type = tdata->tp_info.transport->key.type;
	pj_strset2(&prm.dst_host, tdata->tp_info.dst_name);
	prm.local_if = PJ_TRUE;

	/* If we can't get the local address use best effort and let it pass */
	if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), tdata->pool, &prm) != PJ_SUCCESS) {
		return PJ_SUCCESS;
	}

	/* If the transport it is going out on is different reflect it in the message */
	if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP ||
		tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) {
		transport = multihomed_get_udp_transport(&prm.ret_addr, prm.ret_port);
	}

	/* If no new transport use the one provided by the message */
	if (!transport) {
		transport = tdata->tp_info.transport;
	}

	/* If the message should not be rewritten then abort early */
	if (!multihomed_rewrite_header(&prm.ret_addr, transport)) {
		return PJ_SUCCESS;
	}

	/* Update the transport in case it has changed - we do this now in case we don't want to touch the message above */
	tdata->tp_info.transport = transport;

	/* If the message needs to be updated with new address do so */
	if (tdata->msg->type == PJSIP_REQUEST_MSG || !(cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL)) ||
		pj_strcmp2(&cseq->method.name, "REGISTER")) {
		pjsip_contact_hdr *contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
		if (contact && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
			pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);

			/* prm.ret_addr is allocated from the tdata pool so it is perfectly fine to just do an assignment like this */
			pj_strassign(&uri->host, &prm.ret_addr);
			uri->port = prm.ret_port;

			pjsip_tx_data_invalidate_msg(tdata);
		}
	}

	if (tdata->msg->type == PJSIP_REQUEST_MSG && (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL))) {
		pj_strassign(&via->sent_by.host, &prm.ret_addr);
		via->sent_by.port = prm.ret_port;

		pjsip_tx_data_invalidate_msg(tdata);
	}

	/* Update the SDP if it is present */
	if (tdata->msg->body && ast_sip_is_content_type(&tdata->msg->body->content_type, "application", "sdp") &&
		multihomed_rewrite_sdp(tdata->msg->body->data)) {
		struct pjmedia_sdp_session *sdp = tdata->msg->body->data;
		int stream;

		pj_strassign(&sdp->conn->addr, &prm.ret_addr);

		for (stream = 0; stream < sdp->media_count; ++stream) {
			if (sdp->media[stream]->conn) {
				pj_strassign(&sdp->media[stream]->conn->addr, &prm.ret_addr);
			}
		}

		pjsip_tx_data_invalidate_msg(tdata);
	}

	return PJ_SUCCESS;
}
コード例 #16
0
/*! \brief Internal function which validates provided Contact headers to confirm that they are acceptable, and returns number of contacts */
static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_container *contacts, struct ast_sip_aor *aor, int *added, int *updated, int *deleted)
{
	pjsip_contact_hdr *previous = NULL, *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;
	struct registrar_contact_details details = {
		.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256),
	};

	if (!details.pool) {
		return -1;
	}

	while ((contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) {
		int expiration = registrar_get_expiration(aor, contact, rdata);
		RAII_VAR(struct ast_sip_contact *, existing, NULL, ao2_cleanup);

		if (contact->star) {
			/* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */
			if ((expiration != 0) || previous) {
				pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
				return -1;
			}
			continue;
		} else if (previous && previous->star) {
			/* If there is a previous contact and it is a '*' this is a deal breaker */
			pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
			return -1;
		}
		previous = contact;

		if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) {
			continue;
		}

		details.uri = pjsip_uri_get_uri(contact->uri);

		/* Determine if this is an add, update, or delete for policy enforcement purposes */
		if (!(existing = ao2_callback(contacts, 0, registrar_find_contact, &details))) {
			if (expiration) {
				(*added)++;
			}
		} else if (expiration) {
			(*updated)++;
		} else {
			(*deleted)++;
		}
	}

	/* The provided contacts are acceptable, huzzah! */
	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
	return 0;
}

/*! \brief Callback function which prunes static contacts */
static int registrar_prune_static(void *obj, void *arg, int flags)
{
	struct ast_sip_contact *contact = obj;

	return ast_tvzero(contact->expiration_time) ? CMP_MATCH : 0;
}

/*! \brief Internal function used to delete all contacts from an AOR */
static int registrar_delete_contact(void *obj, void *arg, int flags)
{
	struct ast_sip_contact *contact = obj;
	const char *aor_name = arg;

	ast_sip_location_delete_contact(contact);
	if (!ast_strlen_zero(aor_name)) {
		ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact->uri, aor_name);
		ast_test_suite_event_notify("AOR_CONTACT_REMOVED",
				"Contact: %s\r\n"
				"AOR: %s",
				contact->uri,
				aor_name);
	}

	return 0;
}

/*! \brief Internal function which adds a contact to a response */
static int registrar_add_contact(void *obj, void *arg, int flags)
{
	struct ast_sip_contact *contact = obj;
	pjsip_tx_data *tdata = arg;
	pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(tdata->pool);
	pj_str_t uri;

	pj_strdup2_with_null(tdata->pool, &uri, contact->uri);
	hdr->uri = pjsip_parse_uri(tdata->pool, uri.ptr, uri.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
	hdr->expires = ast_tvdiff_ms(contact->expiration_time, ast_tvnow()) / 1000;

	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);

	return 0;
}

/*! \brief Helper function which adds a Date header to a response */
static void registrar_add_date_header(pjsip_tx_data *tdata)
{
	char date[256];
	struct tm tm;
	time_t t = time(NULL);

	gmtime_r(&t, &tm);
	strftime(date, sizeof(date), "%a, %d %b %Y %T GMT", &tm);

	ast_sip_add_header(tdata, "Date", date);
}
コード例 #17
0
static int digest_create_request_with_auth(const struct ast_sip_auth_vector *auths,
	pjsip_rx_data *challenge, pjsip_tx_data *old_request, pjsip_tx_data **new_request)
{
	pjsip_auth_clt_sess auth_sess;
	pjsip_cseq_hdr *cseq;
	pj_status_t status;
	struct ast_sip_endpoint *endpoint;
	char *id = NULL;
	const char *id_type;
	pjsip_www_authenticate_hdr *auth_hdr;
	struct ast_str *realms;
	pjsip_dialog *dlg;

	dlg = pjsip_rdata_get_dlg(challenge);
	if (dlg) {
		endpoint = ast_sip_dialog_get_endpoint(dlg);
		id = endpoint ? ast_strdupa(ast_sorcery_object_get_id(endpoint)) : NULL;
		ao2_cleanup(endpoint);
		id_type = "Endpoint";
	}
	/* If there was no dialog, then this is probably a REGISTER so no endpoint */
	if (!id) {
		id = ast_alloca(strlen(challenge->pkt_info.src_name) + 7 /* ':' + port + NULL */);
		sprintf(id, "%s:%d", challenge->pkt_info.src_name, challenge->pkt_info.src_port);
		id_type = "Host";
	}

	auth_hdr = get_auth_header(challenge, NULL);
	if (auth_hdr == NULL) {
		ast_log(LOG_ERROR, "%s: '%s': Unable to find authenticate header in challenge.\n",
			id_type, id);
		return -1;
	}

	if (pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(),
				old_request->pool, 0) != PJ_SUCCESS) {
		ast_log(LOG_ERROR, "%s: '%s': Failed to initialize client authentication session\n",
			id_type, id);
		return -1;
	}

	if (set_outbound_authentication_credentials(&auth_sess, auths, challenge, auth_hdr)) {
		ast_log(LOG_WARNING, "%s: '%s': Failed to set authentication credentials\n",
			id_type, id);
#if defined(HAVE_PJSIP_AUTH_CLT_DEINIT)
		/* In case it is not a noop here in the future. */
		pjsip_auth_clt_deinit(&auth_sess);
#endif
		return -1;
	}

	status = pjsip_auth_clt_reinit_req(&auth_sess, challenge, old_request, new_request);
#if defined(HAVE_PJSIP_AUTH_CLT_DEINIT)
	/* Release any cached auths */
	pjsip_auth_clt_deinit(&auth_sess);
#endif

	switch (status) {
	case PJ_SUCCESS:
		/* PJSIP creates a new transaction for new_request (meaning it creates a new
		 * branch). However, it recycles the Call-ID, from-tag, and CSeq from the
		 * original request. Some SIP implementations will not process the new request
		 * since the CSeq is the same as the original request. Incrementing it here
		 * fixes the interop issue
		 */
		cseq = pjsip_msg_find_hdr((*new_request)->msg, PJSIP_H_CSEQ, NULL);
		ast_assert(cseq != NULL);
		++cseq->cseq;
		return 0;
	case PJSIP_ENOCREDENTIAL:
		realms = ast_str_create(32);
		if (realms) {
			ast_str_append(&realms, 0, "%.*s", (int)auth_hdr->challenge.common.realm.slen,
				auth_hdr->challenge.common.realm.ptr);
			while((auth_hdr = get_auth_header(challenge, auth_hdr->next))) {
				ast_str_append(&realms, 0, ",%.*s", (int)auth_hdr->challenge.common.realm.slen,
					auth_hdr->challenge.common.realm.ptr);
			}
		}
		ast_log(LOG_WARNING,
			"%s: '%s': Unable to create request with auth. "
			"No auth credentials for realm(s) '%s' in challenge.\n", id_type, id,
			realms ? ast_str_buffer(realms) : "<unknown>");
		ast_free(realms);
		break;
	case PJSIP_EAUTHSTALECOUNT:
		ast_log(LOG_WARNING,
			"%s: '%s': Unable to create request with auth.  Number of stale retries exceeded.\n",
			id_type, id);
		break;
	case PJSIP_EFAILEDCREDENTIAL:
		ast_log(LOG_WARNING, "%s: '%s': Authentication credentials not accepted by server.\n",
			id_type, id);
		break;
	default:
		ast_log(LOG_WARNING, "%s: '%s': Unable to create request with auth. Unknown failure.\n",
			id_type, id);
		break;
	}

	return -1;
}
コード例 #18
0
static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
{
	RAII_VAR(struct serializer *, ser, NULL, ao2_cleanup);
	struct rx_task_data *task_data;

	RAII_VAR(struct ast_sip_endpoint *, endpoint,
		 ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
	RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
	pjsip_sip_uri *uri;
	char user_name[64], domain_name[64];
	char *configured_aors, *aor_name;

	if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) || !endpoint) {
		return PJ_FALSE;
	}

	if (ast_strlen_zero(endpoint->aors)) {
		/* Short circuit early if the endpoint has no AORs configured on it, which means no registration possible */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
		ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_without_configured_aors");
		ast_log(LOG_WARNING, "Endpoint '%s' has no configured AORs\n", ast_sorcery_object_get_id(endpoint));
		return PJ_TRUE;
	}

	if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) {
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL);
		ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_uri_in_to_received");
		ast_log(LOG_WARNING, "Endpoint '%s' attempted to register to an AOR with a non-SIP URI\n", ast_sorcery_object_get_id(endpoint));
		return PJ_TRUE;
	}

	uri = pjsip_uri_get_uri(rdata->msg_info.to->uri);
	ast_copy_pj_str(user_name, &uri->user, sizeof(user_name));
	ast_copy_pj_str(domain_name, &uri->host, sizeof(domain_name));

	configured_aors = ast_strdupa(endpoint->aors);

	/* Iterate the configured AORs to see if the user or the user+domain match */
	while ((aor_name = strsep(&configured_aors, ","))) {
		char id[AST_UUID_STR_LEN];
		RAII_VAR(struct ast_sip_domain_alias *, alias, NULL, ao2_cleanup);

		snprintf(id, sizeof(id), "%s@%s", user_name, domain_name);
		if (!strcmp(aor_name, id)) {
			break;
		}

		if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) {
			snprintf(id, sizeof(id), "%s@%s", user_name, alias->domain);
			if (!strcmp(aor_name, id)) {
				break;
			}
		}

		if (!strcmp(aor_name, user_name)) {
			break;
		}
	}

	if (ast_strlen_zero(aor_name) || !(aor = ast_sip_location_retrieve_aor(aor_name))) {
		/* The provided AOR name was not found (be it within the configuration or sorcery itself) */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
		ast_sip_report_req_no_support(endpoint, rdata, "registrar_requested_aor_not_found");
		ast_log(LOG_WARNING, "AOR '%s' not found for endpoint '%s'\n", user_name, ast_sorcery_object_get_id(endpoint));
		return PJ_TRUE;
	}

	if (!aor->max_contacts) {
		/* Registration is not permitted for this AOR */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
		ast_sip_report_req_no_support(endpoint, rdata, "registrar_attempt_without_registration_permitted");
		ast_log(LOG_WARNING, "AOR '%s' has no configured max_contacts. Endpoint '%s' unable to register\n",
				ast_sorcery_object_get_id(aor), ast_sorcery_object_get_id(endpoint));
		return PJ_TRUE;
	}

	if (!(ser = serializer_find_or_create(aor_name))) {
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
		ast_sip_report_mem_limit(endpoint, rdata);
		ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not get serializer\n",
			ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor));
		return PJ_TRUE;
	}

	if (!(task_data = rx_task_data_create(rdata, endpoint, aor))) {
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
		ast_sip_report_mem_limit(endpoint, rdata);
		ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not create rx_task_data\n",
			ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor));
		return PJ_TRUE;
	}

	if (ast_sip_push_task(ser->serializer, rx_task, task_data)) {
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
		ast_sip_report_mem_limit(endpoint, rdata);
		ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not serialize task\n",
			ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor));
		ao2_ref(task_data, -1);
	}
	return PJ_TRUE;
}
コード例 #19
0
static int rx_task(void *data)
{
	RAII_VAR(struct rx_task_data *, task_data, data, ao2_cleanup);
	RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);

	int added = 0, updated = 0, deleted = 0;
	pjsip_contact_hdr *contact_hdr = NULL;
	struct registrar_contact_details details = { 0, };
	pjsip_tx_data *tdata;
	pjsip_response_addr addr;
	const char *aor_name = ast_sorcery_object_get_id(task_data->aor);

	/* Retrieve the current contacts, we'll need to know whether to update or not */
	contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor);

	/* So we don't count static contacts against max_contacts we prune them out from the container */
	ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL);

	if (registrar_validate_contacts(task_data->rdata, contacts, task_data->aor, &added, &updated, &deleted)) {
		/* The provided Contact headers do not conform to the specification */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 400, NULL, NULL, NULL);
		ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_invalid_contacts_provided");
		ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n",
				ast_sorcery_object_get_id(task_data->endpoint));
		return PJ_TRUE;
	}

	if ((MAX(added - deleted, 0) + (!task_data->aor->remove_existing ? ao2_container_count(contacts) : 0)) > task_data->aor->max_contacts) {
		/* Enforce the maximum number of contacts */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 403, NULL, NULL, NULL);
		ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_attempt_exceeds_maximum_configured_contacts");
		ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %d\n",
				ast_sorcery_object_get_id(task_data->endpoint), ast_sorcery_object_get_id(task_data->aor), task_data->aor->max_contacts);
		return PJ_TRUE;
	}

	if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) {
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 500, NULL, NULL, NULL);
		return PJ_TRUE;
	}

	/* Iterate each provided Contact header and add, update, or delete */
	while ((contact_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) {
		int expiration;
		char contact_uri[PJSIP_MAX_URL_SIZE];
		RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);

		if (contact_hdr->star) {
			/* A star means to unregister everything, so do so for the possible contacts */
			ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name);
			break;
		}

		if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) {
			/* This registrar only currently supports sip: and sips: URI schemes */
			continue;
		}

		expiration = registrar_get_expiration(task_data->aor, contact_hdr, task_data->rdata);
		details.uri = pjsip_uri_get_uri(contact_hdr->uri);
		pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri));

		if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) {
			/* If they are actually trying to delete a contact that does not exist... be forgiving */
			if (!expiration) {
				ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n",
					contact_uri, aor_name);
				continue;
			}

			ast_sip_location_add_contact(task_data->aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)));
			ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n",
				contact_uri, aor_name, expiration);
			ast_test_suite_event_notify("AOR_CONTACT_ADDED",
					"Contact: %s\r\n"
					"AOR: %s\r\n"
					"Expiration: %d",
					contact_uri,
					aor_name,
					expiration);
		} else if (expiration) {
			RAII_VAR(struct ast_sip_contact *, updated, ast_sorcery_copy(ast_sip_get_sorcery(), contact), ao2_cleanup);
			updated->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1));
			updated->qualify_frequency = task_data->aor->qualify_frequency;
			updated->authenticate_qualify = task_data->aor->authenticate_qualify;

			ast_sip_location_update_contact(updated);
			ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n",
				contact_uri, aor_name, expiration);
			ast_test_suite_event_notify("AOR_CONTACT_REFRESHED",
					"Contact: %s\r\n"
					"AOR: %s\r\n"
					"Expiration: %d",
					contact_uri,
					aor_name,
					expiration);
		} else {
			ast_sip_location_delete_contact(contact);
			ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name);
			ast_test_suite_event_notify("AOR_CONTACT_REMOVED",
					"Contact: %s\r\n"
					"AOR: %s",
					contact_uri,
					aor_name);
		}
	}

	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);

	/* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER
	 * do so
	 */
	if (task_data->aor->remove_existing) {
		ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL);
	}

	/* Update the contacts as things will probably have changed */
	ao2_cleanup(contacts);
	contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor);

	/* Send a response containing all of the contacts (including static) that are present on this AOR */
	if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), task_data->rdata, 200, NULL, &tdata) != PJ_SUCCESS) {
		return PJ_TRUE;
	}

	/* Add the date header to the response, some UAs use this to set their date and time */
	registrar_add_date_header(tdata);

	ao2_callback(contacts, 0, registrar_add_contact, tdata);

	if (pjsip_get_response_addr(tdata->pool, task_data->rdata, &addr) == PJ_SUCCESS) {
		pjsip_endpt_send_response(ast_sip_get_pjsip_endpoint(), &addr, tdata, NULL, NULL);
	} else {
		pjsip_tx_data_dec_ref(tdata);
	}

	return PJ_TRUE;
}
コード例 #20
0
ファイル: res_pjsip_t38.c プロジェクト: cfhb/vlink-cti
/*! \brief Helper function for changing the T.38 state */
static void t38_change_state(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
	struct t38_state *state, enum ast_sip_session_t38state new_state)
{
	enum ast_sip_session_t38state old_state = session->t38state;
	struct ast_control_t38_parameters parameters = { .request_response = 0, };
	pj_time_val delay = { .sec = T38_AUTOMATIC_REJECTION_SECONDS };

	if (old_state == new_state) {
		return;
	}

	session->t38state = new_state;
	ast_debug(2, "T.38 state changed to '%u' from '%u' on channel '%s'\n",
		new_state, old_state,
		session->channel ? ast_channel_name(session->channel) : "<gone>");

	if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &state->timer)) {
		ast_debug(2, "Automatic T.38 rejection on channel '%s' terminated\n",
			session->channel ? ast_channel_name(session->channel) : "<gone>");
		ao2_ref(session, -1);
	}

	if (!session->channel) {
		return;
	}

	switch (new_state) {
	case T38_PEER_REINVITE:
		ao2_ref(session, +1);
		if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &state->timer, &delay) != PJ_SUCCESS) {
			ast_log(LOG_WARNING, "Scheduling of automatic T.38 rejection for channel '%s' failed\n",
				ast_channel_name(session->channel));
			ao2_ref(session, -1);
		}
		parameters = state->their_parms;
		parameters.max_ifp = ast_udptl_get_far_max_ifp(session_media->udptl);
		parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
		ast_udptl_set_tag(session_media->udptl, "%s", ast_channel_name(session->channel));
		break;
	case T38_ENABLED:
		parameters = state->their_parms;
		parameters.max_ifp = ast_udptl_get_far_max_ifp(session_media->udptl);
		parameters.request_response = AST_T38_NEGOTIATED;
		ast_udptl_set_tag(session_media->udptl, "%s", ast_channel_name(session->channel));
		break;
	case T38_REJECTED:
	case T38_DISABLED:
		if (old_state == T38_ENABLED) {
			parameters.request_response = AST_T38_TERMINATED;
		} else if (old_state == T38_LOCAL_REINVITE) {
			parameters.request_response = AST_T38_REFUSED;
		}
		break;
	case T38_LOCAL_REINVITE:
		/* wait until we get a peer response before responding to local reinvite */
		break;
	case T38_MAX_ENUM:
		/* Well, that shouldn't happen */
		ast_assert(0);
		break;
	}

	if (parameters.request_response) {
		ast_queue_control_data(session->channel, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
	}
}

/*! \brief Task function which rejects a T.38 re-invite and resumes handling it */
static int t38_automatic_reject(void *obj)
{
	RAII_VAR(struct ast_sip_session *, session, obj, ao2_cleanup);
	RAII_VAR(struct ast_datastore *, datastore, ast_sip_session_get_datastore(session, "t38"), ao2_cleanup);
	RAII_VAR(struct ast_sip_session_media *, session_media, ao2_find(session->media, "image", OBJ_KEY), ao2_cleanup);

	if (!datastore) {
		return 0;
	}

	ast_debug(2, "Automatically rejecting T.38 request on channel '%s'\n",
		session->channel ? ast_channel_name(session->channel) : "<gone>");

	t38_change_state(session, session_media, datastore->data, T38_REJECTED);
	ast_sip_session_resume_reinvite(session);

	return 0;
}
コード例 #21
0
/*!
 * \brief Create a pjsip transport.
 */
static int transport_create(void *data)
{
	struct transport_create_data *create_data = data;
	struct ws_transport *newtransport = NULL;

	pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
	struct pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(endpt);

	pj_pool_t *pool;
	pj_str_t buf;
	pj_status_t status;

	newtransport = ao2_t_alloc_options(sizeof(*newtransport), transport_dtor,
			AO2_ALLOC_OPT_LOCK_NOLOCK, "pjsip websocket transport");
	if (!newtransport) {
		ast_log(LOG_ERROR, "Failed to allocate WebSocket transport.\n");
		goto on_error;
	}

	newtransport->transport.endpt = endpt;

	if (!(pool = pjsip_endpt_create_pool(endpt, "ws", 512, 512))) {
		ast_log(LOG_ERROR, "Failed to allocate WebSocket endpoint pool.\n");
		goto on_error;
	}

	newtransport->transport.pool = pool;
	newtransport->ws_session = create_data->ws_session;

	/* Keep the session until transport dies */
	ast_websocket_ref(newtransport->ws_session);

	status = pj_atomic_create(pool, 0, &newtransport->transport.ref_cnt);
	if (status != PJ_SUCCESS) {
		goto on_error;
	}

	status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &newtransport->transport.lock);
	if (status != PJ_SUCCESS) {
		goto on_error;
	}

	pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ast_sockaddr_stringify(ast_websocket_remote_address(newtransport->ws_session))), &newtransport->transport.key.rem_addr);
	newtransport->transport.key.rem_addr.addr.sa_family = pj_AF_INET();
	newtransport->transport.key.type = ast_websocket_is_secure(newtransport->ws_session) ? transport_type_wss : transport_type_ws;

	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);

	newtransport->transport.type_name = (char *)pjsip_transport_get_type_name(newtransport->transport.key.type);
	newtransport->transport.flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)newtransport->transport.key.type);
	newtransport->transport.info = (char *)pj_pool_alloc(newtransport->transport.pool, 64);

	newtransport->transport.tpmgr = tpmgr;
	newtransport->transport.send_msg = &ws_send_msg;
	newtransport->transport.destroy = &ws_destroy;

	status = pjsip_transport_register(newtransport->transport.tpmgr,
			(pjsip_transport *)newtransport);
	if (status != PJ_SUCCESS) {
		goto on_error;
	}

	/* Add a reference for pjsip transport manager */
	ao2_ref(newtransport, +1);

	newtransport->rdata.tp_info.transport = &newtransport->transport;
	newtransport->rdata.tp_info.pool = pjsip_endpt_create_pool(endpt, "rtd%p",
		PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC);
	if (!newtransport->rdata.tp_info.pool) {
		ast_log(LOG_ERROR, "Failed to allocate WebSocket rdata.\n");
		pjsip_transport_destroy((pjsip_transport *)newtransport);
		goto on_error;
	}

	create_data->transport = newtransport;
	return 0;

on_error:
	ao2_cleanup(newtransport);
	return -1;
}
コード例 #22
0
ファイル: pjsip_distributor.c プロジェクト: scudella/asterisk
static pj_bool_t distributor(pjsip_rx_data *rdata)
{
	pjsip_dialog *dlg;
	struct distributor_dialog_data *dist = NULL;
	struct ast_taskprocessor *serializer = NULL;
	pjsip_rx_data *clone;

	if (!ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
		/*
		 * Ignore everything until we are fully booted.  Let the
		 * peer retransmit messages until we are ready.
		 */
		return PJ_TRUE;
	}

	dlg = find_dialog(rdata);
	if (dlg) {
		ast_debug(3, "Searching for serializer associated with dialog %s for %s\n",
			dlg->obj_name, pjsip_rx_data_get_info(rdata));
		dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY);
		if (dist) {
			ao2_lock(dist);
			serializer = ao2_bump(dist->serializer);
			ao2_unlock(dist);
			if (serializer) {
				ast_debug(3, "Found serializer %s associated with dialog %s\n",
					ast_taskprocessor_name(serializer), dlg->obj_name);
			}
		}
	}

	if (serializer) {
		/* We have a serializer so we know where to send the message. */
	} else if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG) {
		ast_debug(3, "No dialog serializer for %s.  Using request transaction as basis.\n",
			pjsip_rx_data_get_info(rdata));
		serializer = find_request_serializer(rdata);
		if (!serializer) {
			/*
			 * Pick a serializer for the unmatched response.
			 * We couldn't determine what serializer originally
			 * sent the request or the serializer is gone.
			 */
			serializer = ast_sip_get_distributor_serializer(rdata);
		}
	} else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method)
		|| !pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_bye_method)) {
		/* We have a BYE or CANCEL request without a serializer. */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata,
			PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, NULL, NULL, NULL);
		ao2_cleanup(dist);
		return PJ_TRUE;
	} else {
		if (ast_taskprocessor_alert_get()) {
			/*
			 * When taskprocessors get backed up, there is a good chance that
			 * we are being overloaded and need to defer adding new work to
			 * the system.  To defer the work we will ignore the request and
			 * rely on the peer's transport layer to retransmit the message.
			 * We usually work off the overload within a few seconds.  The
			 * alternative is to send back a 503 response to these requests
			 * and be done with it.
			 */
			ast_debug(3, "Taskprocessor overload alert: Ignoring '%s'.\n",
				pjsip_rx_data_get_info(rdata));
			ao2_cleanup(dist);
			return PJ_TRUE;
		}

		/* Pick a serializer for the out-of-dialog request. */
		serializer = ast_sip_get_distributor_serializer(rdata);
	}

	if (pjsip_rx_data_clone(rdata, 0, &clone) != PJ_SUCCESS) {
		ast_taskprocessor_unreference(serializer);
		ao2_cleanup(dist);
		return PJ_TRUE;
	}

	if (dist) {
		ao2_lock(dist);
		clone->endpt_info.mod_data[endpoint_mod.id] = ao2_bump(dist->endpoint);
		ao2_unlock(dist);
		ao2_cleanup(dist);
	}

	if (ast_sip_push_task(serializer, distribute, clone)) {
		ao2_cleanup(clone->endpt_info.mod_data[endpoint_mod.id]);
		pjsip_rx_data_free_cloned(clone);
	}

	ast_taskprocessor_unreference(serializer);

	return PJ_TRUE;
}
コード例 #23
0
ファイル: pjsip_distributor.c プロジェクト: scudella/asterisk
static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata)
{
	struct ast_sip_endpoint *endpoint;
	struct unidentified_request *unid;
	int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD;

	endpoint = rdata->endpt_info.mod_data[endpoint_mod.id];
	if (endpoint) {
		/*
		 * ao2_find with OBJ_UNLINK always write locks the container before even searching
		 * for the object.  Since the majority case is that the object won't be found, do
		 * the find without OBJ_UNLINK to prevent the unnecessary write lock, then unlink
		 * if needed.
		 */
		unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY);
		if (unid) {
			ao2_unlink(unidentified_requests, unid);
			ao2_ref(unid, -1);
		}
		apply_acls(rdata);
		return PJ_FALSE;
	}

	endpoint = ast_sip_identify_endpoint(rdata);
	if (endpoint) {
		unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY);
		if (unid) {
			ao2_unlink(unidentified_requests, unid);
			ao2_ref(unid, -1);
		}
	}

	if (!endpoint) {
		/* always use an artificial endpoint - per discussion no reason
		   to have "alwaysauthreject" as an option.  It is felt using it
		   was a bug fix and it is not needed since we are not worried about
		   breaking old stuff and we really don't want to enable the discovery
		   of SIP accounts */
		endpoint = ast_sip_get_artificial_endpoint();
	}

	/* endpoint ref held by mod_data[] */
	rdata->endpt_info.mod_data[endpoint_mod.id] = endpoint;

	if (endpoint == artificial_endpoint && !is_ack) {
		char name[AST_UUID_STR_LEN] = "";
		pjsip_uri *from = rdata->msg_info.from->uri;

		if (PJSIP_URI_SCHEME_IS_SIP(from) || PJSIP_URI_SCHEME_IS_SIPS(from)) {
			pjsip_sip_uri *sip_from = pjsip_uri_get_uri(from);
			ast_copy_pj_str(name, &sip_from->user, sizeof(name));
		}

		unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY);
		if (unid) {
			check_endpoint(rdata, unid, name);
			ao2_ref(unid, -1);
		} else if (using_auth_username) {
			ao2_wrlock(unidentified_requests);
			/* Checking again with the write lock held allows us to eliminate the DUPS_REPLACE and sort_fn */
			unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name,
				OBJ_SEARCH_KEY | OBJ_NOLOCK);
			if (unid) {
				check_endpoint(rdata, unid, name);
			} else {
				unid = ao2_alloc_options(sizeof(*unid) + strlen(rdata->pkt_info.src_name) + 1,
					NULL, AO2_ALLOC_OPT_LOCK_RWLOCK);
				if (!unid) {
					ao2_unlock(unidentified_requests);
					pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
					return PJ_TRUE;
				}
				strcpy(unid->src_name, rdata->pkt_info.src_name); /* Safe */
				unid->first_seen = ast_tvnow();
				unid->count = 1;
				ao2_link_flags(unidentified_requests, unid, OBJ_NOLOCK);
			}
			ao2_ref(unid, -1);
			ao2_unlock(unidentified_requests);
		} else {
			log_failed_request(rdata, "No matching endpoint found", 0, 0);
			ast_sip_report_invalid_endpoint(name, rdata);
		}
	}

	apply_acls(rdata);
	return PJ_FALSE;
}
コード例 #24
0
static struct ast_sip_aor *find_registrar_aor(struct pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint)
{
	struct ast_sip_aor *aor = NULL;
	char *aor_name = NULL;
	char *domain_name;
	char *username = NULL;
	int i;

	for (i = 0; i < AST_VECTOR_SIZE(&endpoint->ident_method_order); ++i) {
		pjsip_sip_uri *uri;
		pjsip_authorization_hdr *header = NULL;

		switch (AST_VECTOR_GET(&endpoint->ident_method_order, i)) {
		case AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME:
			uri = pjsip_uri_get_uri(rdata->msg_info.to->uri);

			domain_name = ast_alloca(uri->host.slen + 1);
			ast_copy_pj_str(domain_name, &uri->host, uri->host.slen + 1);
			username = ast_alloca(uri->user.slen + 1);
			ast_copy_pj_str(username, &uri->user, uri->user.slen + 1);

			/*
			 * We may want to match without any user options getting
			 * in the way.
			 */
			AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(username);

			aor_name = find_aor_name(username, domain_name, endpoint->aors);
			if (aor_name) {
				ast_debug(3, "Matched aor '%s' by To username\n", aor_name);
			}
			break;
		case AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME:
			while ((header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION,
				header ? header->next : NULL))) {
				if (header && !pj_stricmp2(&header->scheme, "digest")) {
					username = ast_alloca(header->credential.digest.username.slen + 1);
					ast_copy_pj_str(username, &header->credential.digest.username, header->credential.digest.username.slen + 1);
					domain_name = ast_alloca(header->credential.digest.realm.slen + 1);
					ast_copy_pj_str(domain_name, &header->credential.digest.realm, header->credential.digest.realm.slen + 1);

					aor_name = find_aor_name(username, domain_name, endpoint->aors);
					if (aor_name) {
						ast_debug(3, "Matched aor '%s' by Authentication username\n", aor_name);
						break;
					}
				}
			}
			break;
		default:
			continue;
		}

		if (aor_name) {
			break;
		}
	}

	if (ast_strlen_zero(aor_name) || !(aor = ast_sip_location_retrieve_aor(aor_name))) {
		/* The provided AOR name was not found (be it within the configuration or sorcery itself) */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
		ast_sip_report_req_no_support(endpoint, rdata, "registrar_requested_aor_not_found");
		ast_log(LOG_WARNING, "AOR '%s' not found for endpoint '%s'\n",
			username ?: "", ast_sorcery_object_get_id(endpoint));
	}
コード例 #25
0
static int register_aor_core(pjsip_rx_data *rdata,
	struct ast_sip_endpoint *endpoint,
	struct ast_sip_aor *aor,
	const char *aor_name,
	struct ao2_container *contacts)
{
	static const pj_str_t USER_AGENT = { "User-Agent", 10 };

	int added = 0, updated = 0, deleted = 0;
	pjsip_contact_hdr *contact_hdr = NULL;
	struct registrar_contact_details details = { 0, };
	pjsip_tx_data *tdata;
	RAII_VAR(struct ast_str *, path_str, NULL, ast_free);
	struct ast_sip_contact *response_contact;
	char *user_agent = NULL;
	pjsip_user_agent_hdr *user_agent_hdr;
	pjsip_expires_hdr *expires_hdr;
	pjsip_via_hdr *via_hdr;
	pjsip_via_hdr *via_hdr_last;
	char *via_addr = NULL;
	int via_port = 0;
	pjsip_cid_hdr *call_id_hdr;
	char *call_id = NULL;
	size_t alloc_size;

	/* So we don't count static contacts against max_contacts we prune them out from the container */
	ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL);

	if (registrar_validate_contacts(rdata, contacts, aor, &added, &updated, &deleted)) {
		/* The provided Contact headers do not conform to the specification */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL);
		ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided");
		ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n",
				ast_sorcery_object_get_id(endpoint));
		return PJ_TRUE;
	}

	if (registrar_validate_path(rdata, aor, &path_str)) {
		/* Ensure that intervening proxies did not make invalid modifications to the request */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 420, NULL, NULL, NULL);
		ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n",
				ast_sorcery_object_get_id(endpoint));
		return PJ_TRUE;
	}

	if ((MAX(added - deleted, 0) + (!aor->remove_existing ? ao2_container_count(contacts) : 0)) > aor->max_contacts) {
		/* Enforce the maximum number of contacts */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
		ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts");
		ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %u\n",
				ast_sorcery_object_get_id(endpoint), aor_name, aor->max_contacts);
		return PJ_TRUE;
	}

	if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) {
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
		return PJ_TRUE;
	}

	user_agent_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &USER_AGENT, NULL);
	if (user_agent_hdr) {
		alloc_size = pj_strlen(&user_agent_hdr->hvalue) + 1;
		user_agent = ast_alloca(alloc_size);
		ast_copy_pj_str(user_agent, &user_agent_hdr->hvalue, alloc_size);
	}

	/* Find the first Via header */
	via_hdr = via_hdr_last = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, NULL);
	if (via_hdr) {
		/* Find the last Via header */
		while ( (via_hdr = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg,
				PJSIP_H_VIA, via_hdr->next)) != NULL) {
			via_hdr_last = via_hdr;
		}
		alloc_size = pj_strlen(&via_hdr_last->sent_by.host) + 1;
		via_addr = ast_alloca(alloc_size);
		ast_copy_pj_str(via_addr, &via_hdr_last->sent_by.host, alloc_size);
		via_port=via_hdr_last->sent_by.port;
	}

	call_id_hdr = (pjsip_cid_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CALL_ID, NULL);
	if (call_id_hdr) {
		alloc_size = pj_strlen(&call_id_hdr->id) + 1;
		call_id = ast_alloca(alloc_size);
		ast_copy_pj_str(call_id, &call_id_hdr->id, alloc_size);
	}

	/* Iterate each provided Contact header and add, update, or delete */
	while ((contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) {
		int expiration;
		char contact_uri[pjsip_max_url_size];
		RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);

		if (contact_hdr->star) {
			/* A star means to unregister everything, so do so for the possible contacts */
			ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name);
			break;
		}

		if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) {
			/* This registrar only currently supports sip: and sips: URI schemes */
			continue;
		}

		expiration = registrar_get_expiration(aor, contact_hdr, rdata);
		details.uri = pjsip_uri_get_uri(contact_hdr->uri);
		pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri));

		if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) {
			/* If they are actually trying to delete a contact that does not exist... be forgiving */
			if (!expiration) {
				ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n",
					contact_uri, aor_name);
				continue;
			}

			if (ast_sip_location_add_contact_nolock(aor, contact_uri, ast_tvadd(ast_tvnow(),
				ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL,
					user_agent, via_addr, via_port, call_id, endpoint)) {
				ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n",
						contact_uri, aor_name);
				continue;
			}

			ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n",
				contact_uri, aor_name, expiration);
			ast_test_suite_event_notify("AOR_CONTACT_ADDED",
					"Contact: %s\r\n"
					"AOR: %s\r\n"
					"Expiration: %d\r\n"
					"UserAgent: %s",
					contact_uri,
					aor_name,
					expiration,
					user_agent);
		} else if (expiration) {
			struct ast_sip_contact *contact_update;

			contact_update = ast_sorcery_copy(ast_sip_get_sorcery(), contact);
			if (!contact_update) {
				ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n",
					contact->uri, expiration);
				continue;
			}

			contact_update->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1));
			contact_update->qualify_frequency = aor->qualify_frequency;
			contact_update->authenticate_qualify = aor->authenticate_qualify;
			if (path_str) {
				ast_string_field_set(contact_update, path, ast_str_buffer(path_str));
			}
			if (user_agent) {
				ast_string_field_set(contact_update, user_agent, user_agent);
			}
			if (!ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
				ast_string_field_set(contact_update, reg_server, ast_config_AST_SYSTEM_NAME);
			}

			if (ast_sip_location_update_contact(contact_update)) {
				ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n",
					contact->uri, expiration);
				ast_sip_location_delete_contact(contact);
				continue;
			}
			ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n",
				contact_uri, aor_name, expiration);
			ast_test_suite_event_notify("AOR_CONTACT_REFRESHED",
					"Contact: %s\r\n"
					"AOR: %s\r\n"
					"Expiration: %d\r\n"
					"UserAgent: %s",
					contact_uri,
					aor_name,
					expiration,
					contact_update->user_agent);
			ao2_cleanup(contact_update);
		} else {
			/* We want to report the user agent that was actually in the removed contact */
			ast_sip_location_delete_contact(contact);
			ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name);
			ast_test_suite_event_notify("AOR_CONTACT_REMOVED",
					"Contact: %s\r\n"
					"AOR: %s\r\n"
					"UserAgent: %s",
					contact_uri,
					aor_name,
					contact->user_agent);
		}
	}

	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);

	/* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER
	 * do so
	 */
	if (aor->remove_existing) {
		ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL);
	}

	/* Re-retrieve contacts.  Caller will clean up the original container. */
	contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor);
	response_contact = ao2_callback(contacts, 0, NULL, NULL);

	/* Send a response containing all of the contacts (including static) that are present on this AOR */
	if (ast_sip_create_response(rdata, 200, response_contact, &tdata) != PJ_SUCCESS) {
		ao2_cleanup(response_contact);
		ao2_cleanup(contacts);
		return PJ_TRUE;
	}
	ao2_cleanup(response_contact);

	/* Add the date header to the response, some UAs use this to set their date and time */
	registrar_add_date_header(tdata);

	ao2_callback(contacts, 0, registrar_add_contact, tdata);
	ao2_cleanup(contacts);

	if ((expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) {
		expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(aor, NULL, rdata));
		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr);
	}

	ast_sip_send_stateful_response(rdata, tdata, endpoint);

	return PJ_TRUE;
}
コード例 #26
0
ファイル: pjsip_distributor.c プロジェクト: scudella/asterisk
static int distribute(void *data)
{
	static pjsip_process_rdata_param param = {
		.start_mod = &distributor_mod,
		.idx_after_start = 1,
	};
	pj_bool_t handled = PJ_FALSE;
	pjsip_rx_data *rdata = data;
	int is_request = rdata->msg_info.msg->type == PJSIP_REQUEST_MSG;
	int is_ack = is_request ? rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD : 0;
	struct ast_sip_endpoint *endpoint;

	pjsip_endpt_process_rx_data(ast_sip_get_pjsip_endpoint(), rdata, &param, &handled);
	if (!handled && is_request && !is_ack) {
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 501, NULL, NULL, NULL);
	}

	/* The endpoint_mod stores an endpoint reference in the mod_data of rdata. This
	 * is the only appropriate spot to actually decrement the reference.
	 */
	endpoint = rdata->endpt_info.mod_data[endpoint_mod.id];
	ao2_cleanup(endpoint);
	pjsip_rx_data_free_cloned(rdata);
	return 0;
}

struct ast_sip_endpoint *ast_pjsip_rdata_get_endpoint(pjsip_rx_data *rdata)
{
	struct ast_sip_endpoint *endpoint = rdata->endpt_info.mod_data[endpoint_mod.id];
	if (endpoint) {
		ao2_ref(endpoint, +1);
	}
	return endpoint;
}

static int suspects_sort(const void *obj, const void *arg, int flags)
{
	const struct unidentified_request *object_left = obj;
	const struct unidentified_request *object_right = arg;
	const char *right_key = arg;
	int cmp;

	switch (flags & OBJ_SEARCH_MASK) {
	case OBJ_SEARCH_OBJECT:
		right_key = object_right->src_name;
		/* Fall through */
	case OBJ_SEARCH_KEY:
		cmp = strcmp(object_left->src_name, right_key);
		break;
	case OBJ_SEARCH_PARTIAL_KEY:
		cmp = strncmp(object_left->src_name, right_key, strlen(right_key));
		break;
	default:
		cmp = 0;
		break;
	}
	return cmp;
}

static int suspects_compare(void *obj, void *arg, int flags)
{
	const struct unidentified_request *object_left = obj;
	const struct unidentified_request *object_right = arg;
	const char *right_key = arg;
	int cmp = 0;

	switch (flags & OBJ_SEARCH_MASK) {
	case OBJ_SEARCH_OBJECT:
		right_key = object_right->src_name;
		/* Fall through */
	case OBJ_SEARCH_KEY:
		if (strcmp(object_left->src_name, right_key) == 0) {
			cmp = CMP_MATCH;
		}
		break;
	case OBJ_SEARCH_PARTIAL_KEY:
		if (strncmp(object_left->src_name, right_key, strlen(right_key)) == 0) {
			cmp = CMP_MATCH;
		}
		break;
	default:
		cmp = 0;
		break;
	}
	return cmp;
}

static int suspects_hash(const void *obj, int flags)
{
	const struct unidentified_request *object;
	const char *key;

	switch (flags & OBJ_SEARCH_MASK) {
	case OBJ_SEARCH_KEY:
		key = obj;
		break;
	case OBJ_SEARCH_OBJECT:
		object = obj;
		key = object->src_name;
		break;
	default:
		/* Hash can only work on something with a full key. */
		ast_assert(0);
		return 0;
	}
	return ast_str_hash(key);
}

static struct ao2_container *cli_unid_get_container(const char *regex)
{
	struct ao2_container *s_container;

	s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
		suspects_sort, suspects_compare);
	if (!s_container) {
		return NULL;
	}

	if (ao2_container_dup(s_container, unidentified_requests, 0)) {
		ao2_ref(s_container, -1);
		return NULL;
	}

	return s_container;
}

static int cli_unid_iterate(void *container, ao2_callback_fn callback, void *args)
{
	ao2_callback(container, 0, callback, args);

	return 0;
}
コード例 #27
0
ファイル: config_transport.c プロジェクト: hardikk/asterisk
/*! \brief Apply handler for transports */
static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
{
	struct ast_sip_transport *transport = obj;
	RAII_VAR(struct ast_sip_transport *, existing, ast_sorcery_retrieve_by_id(sorcery, "transport", ast_sorcery_object_get_id(obj)), ao2_cleanup);
	pj_status_t res = -1;

	if (!existing || !existing->state) {
		if (!(transport->state = ao2_alloc(sizeof(*transport->state), transport_state_destroy))) {
			ast_log(LOG_ERROR, "Transport state for '%s' could not be allocated\n", ast_sorcery_object_get_id(obj));
			return -1;
		}
	} else {
		transport->state = existing->state;
		ao2_ref(transport->state, +1);
	}

	/* Once active a transport can not be reconfigured */
	if (transport->state->transport || transport->state->factory) {
		return -1;
	}

	if (transport->host.addr.sa_family != PJ_AF_INET && transport->host.addr.sa_family != PJ_AF_INET6) {
		ast_log(LOG_ERROR, "Transport '%s' could not be started as binding not specified\n", ast_sorcery_object_get_id(obj));
		return -1;
	}

	/* Set default port if not present */
	if (!pj_sockaddr_get_port(&transport->host)) {
		pj_sockaddr_set_port(&transport->host, (transport->type == AST_TRANSPORT_TLS) ? 5061 : 5060);
	}

	/* Now that we know what address family we can set up a dnsmgr refresh for the external media address if present */
	if (!ast_strlen_zero(transport->external_signaling_address)) {
		if (transport->host.addr.sa_family == pj_AF_INET()) {
			transport->external_address.ss.ss_family = AF_INET;
		} else if (transport->host.addr.sa_family == pj_AF_INET6()) {
			transport->external_address.ss.ss_family = AF_INET6;
		} else {
			ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external signaling address\n",
					ast_sorcery_object_get_id(obj));
			return -1;
		}

		if (ast_dnsmgr_lookup(transport->external_signaling_address, &transport->external_address, &transport->external_address_refresher, NULL) < 0) {
			ast_log(LOG_ERROR, "Could not create dnsmgr for external signaling address on '%s'\n", ast_sorcery_object_get_id(obj));
			return -1;
		}
	}

	if (transport->type == AST_TRANSPORT_UDP) {
		if (transport->host.addr.sa_family == pj_AF_INET()) {
			res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(), &transport->host.ipv4, NULL, transport->async_operations, &transport->state->transport);
		} else if (transport->host.addr.sa_family == pj_AF_INET6()) {
			res = pjsip_udp_transport_start6(ast_sip_get_pjsip_endpoint(), &transport->host.ipv6, NULL, transport->async_operations, &transport->state->transport);
		}

		if (res == PJ_SUCCESS && (transport->tos || transport->cos)) {
			pj_sock_t sock;
			pj_qos_params qos_params;

			sock = pjsip_udp_transport_get_socket(transport->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;

		pjsip_tcp_transport_cfg_default(&cfg, transport->host.addr.sa_family);
		cfg.bind_addr = transport->host;
		cfg.async_cnt = transport->async_operations;
		set_qos(transport, &cfg.qos_params);

		res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg, &transport->state->factory);
	} else if (transport->type == AST_TRANSPORT_TLS) {
		transport->tls.ca_list_file = pj_str((char*)transport->ca_list_file);
		transport->tls.cert_file = pj_str((char*)transport->cert_file);
		transport->tls.privkey_file = pj_str((char*)transport->privkey_file);
		transport->tls.password = pj_str((char*)transport->password);
		set_qos(transport, &transport->tls.qos_params);

		res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &transport->tls, &transport->host, NULL, transport->async_operations, &transport->state->factory);
	} else if ((transport->type == AST_TRANSPORT_WS) || (transport->type == AST_TRANSPORT_WSS)) {
		if (transport->cos || transport->tos) {
			ast_log(LOG_WARNING, "TOS and COS values ignored for websocket transport\n");
		}
		res = PJ_SUCCESS;
	}

	if (res != PJ_SUCCESS) {
		char msg[PJ_ERR_MSG_SIZE];

		pjsip_strerror(res, msg, sizeof(msg));
		ast_log(LOG_ERROR, "Transport '%s' could not be started: %s\n", ast_sorcery_object_get_id(obj), msg);
		return -1;
	}
	return 0;
}
コード例 #28
0
static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
{
	pjsip_tpmgr_fla2_param prm;
	pjsip_cseq_hdr *cseq;
	pjsip_via_hdr *via;

	/* Use the destination information to determine what local interface this message will go out on */
	pjsip_tpmgr_fla2_param_default(&prm);
	prm.tp_type = tdata->tp_info.transport->key.type;
	pj_strset2(&prm.dst_host, tdata->tp_info.dst_name);
	prm.local_if = PJ_TRUE;

	/* If we can't get the local address use best effort and let it pass */
	if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), tdata->pool, &prm) != PJ_SUCCESS) {
		return PJ_SUCCESS;
	}

	/* The port in the message should always be that of the original transport */
	prm.ret_port = tdata->tp_info.transport->local_name.port;

	/* If the IP source differs from the existing transport see if we need to update it */
	if (pj_strcmp(&prm.ret_addr, &tdata->tp_info.transport->local_name.host)) {

		/* If the transport it is going out on is different reflect it in the message */
		if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP ||
			tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) {
			pjsip_transport *transport;

			transport = multihomed_get_udp_transport(&prm.ret_addr, prm.ret_port);

			if (transport) {
				tdata->tp_info.transport = transport;
			}
		}

		/* If the chosen transport is not bound to any we can't use the source address as it won't get back to us */
		if (!multihomed_bound_any(tdata->tp_info.transport)) {
			pj_strassign(&prm.ret_addr, &tdata->tp_info.transport->local_name.host);
		}
	} else {
		/* The transport chosen will deliver this but ensure it is updated with the right information */
		pj_strassign(&prm.ret_addr, &tdata->tp_info.transport->local_name.host);
	}

	/* If the message needs to be updated with new address do so */
	if (tdata->msg->type == PJSIP_REQUEST_MSG || !(cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL)) ||
		pj_strcmp2(&cseq->method.name, "REGISTER")) {
		pjsip_contact_hdr *contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
		if (contact && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))
			&& !(tdata->msg->type == PJSIP_RESPONSE_MSG && tdata->msg->line.status.code / 100 == 3)) {
			pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);

			/* prm.ret_addr is allocated from the tdata pool OR the transport so it is perfectly fine to just do an assignment like this */
			pj_strassign(&uri->host, &prm.ret_addr);
			uri->port = prm.ret_port;
			ast_debug(4, "Re-wrote Contact URI host/port to %.*s:%d\n",
				(int)pj_strlen(&uri->host), pj_strbuf(&uri->host), uri->port);

			pjsip_tx_data_invalidate_msg(tdata);
		}
	}

	if (tdata->msg->type == PJSIP_REQUEST_MSG && (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL))) {
		pj_strassign(&via->sent_by.host, &prm.ret_addr);
		via->sent_by.port = prm.ret_port;

		pjsip_tx_data_invalidate_msg(tdata);
	}

	/* Update the SDP if it is present */
	if (tdata->msg->body && ast_sip_is_content_type(&tdata->msg->body->content_type, "application", "sdp") &&
		multihomed_rewrite_sdp(tdata->msg->body->data)) {
		struct pjmedia_sdp_session *sdp = tdata->msg->body->data;
		int stream;

		pj_strassign(&sdp->conn->addr, &prm.ret_addr);

		for (stream = 0; stream < sdp->media_count; ++stream) {
			if (sdp->media[stream]->conn) {
				pj_strassign(&sdp->media[stream]->conn->addr, &prm.ret_addr);
			}
		}

		pjsip_tx_data_invalidate_msg(tdata);
	}

	return PJ_SUCCESS;
}
コード例 #29
0
ファイル: res_hep_pjsip.c プロジェクト: asterisk/asterisk
static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
{
	char local_buf[256];
	char remote_buf[256];
	char *uuid;
	struct hepv3_capture_info *capture_info;
	pjsip_cid_hdr *cid_hdr;
	pjsip_from_hdr *from_hdr;
	pjsip_to_hdr *to_hdr;

	capture_info = hepv3_create_capture_info(tdata->buf.start, (size_t)(tdata->buf.cur - tdata->buf.start));
	if (!capture_info) {
		return PJ_SUCCESS;
	}

	if (!(tdata->tp_info.transport->flag & PJSIP_TRANSPORT_RELIABLE)) {
		pjsip_tpmgr_fla2_param prm;

		/* Attempt to determine what IP address will we send this packet out of */
		pjsip_tpmgr_fla2_param_default(&prm);
		prm.tp_type = tdata->tp_info.transport->key.type;
		pj_strset2(&prm.dst_host, tdata->tp_info.dst_name);
		prm.local_if = PJ_TRUE;

		/* If we can't get the local address use what we have already */
		if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), tdata->pool, &prm) != PJ_SUCCESS) {
			pj_sockaddr_print(&tdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3);
		} else {
			if (prm.tp_type & PJSIP_TRANSPORT_IPV6) {
				snprintf(local_buf, sizeof(local_buf), "[%.*s]:%hu",
					(int)pj_strlen(&prm.ret_addr),
					pj_strbuf(&prm.ret_addr),
					prm.ret_port);
			} else {
				snprintf(local_buf, sizeof(local_buf), "%.*s:%hu",
					(int)pj_strlen(&prm.ret_addr),
					pj_strbuf(&prm.ret_addr),
					prm.ret_port);
			}
		}
	} else {
		/* For reliable transports they can only ever come from the transport
		 * local address.
		 */
		pj_sockaddr_print(&tdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3);
	}

	pj_sockaddr_print(&tdata->tp_info.dst_addr, remote_buf, sizeof(remote_buf), 3);

	cid_hdr = PJSIP_MSG_CID_HDR(tdata->msg);
	from_hdr = PJSIP_MSG_FROM_HDR(tdata->msg);
	to_hdr = PJSIP_MSG_TO_HDR(tdata->msg);

	uuid = assign_uuid(&cid_hdr->id, &to_hdr->tag, &from_hdr->tag);
	if (!uuid) {
		ao2_ref(capture_info, -1);
		return PJ_SUCCESS;
	}

	ast_sockaddr_parse(&capture_info->src_addr, local_buf, PARSE_PORT_REQUIRE);
	ast_sockaddr_parse(&capture_info->dst_addr, remote_buf, PARSE_PORT_REQUIRE);

	capture_info->protocol_id = transport_to_protocol_id(tdata->tp_info.transport);
	capture_info->capture_time = ast_tvnow();
	capture_info->capture_type = HEPV3_CAPTURE_TYPE_SIP;
	capture_info->uuid = uuid;
	capture_info->zipped = 0;

	hepv3_send_packet(capture_info);

	return PJ_SUCCESS;
}
コード例 #30
0
ファイル: config_transport.c プロジェクト: GGGO/asterisk
/*! \brief Apply handler for transports */
static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
{
	struct ast_sip_transport *transport = obj;
	const char *transport_id = ast_sorcery_object_get_id(obj);
	RAII_VAR(struct ao2_container *, states, transport_states, states_cleanup);
	RAII_VAR(struct internal_state *, temp_state, NULL, ao2_cleanup);
	RAII_VAR(struct internal_state *, perm_state, NULL, ao2_cleanup);
	RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
	pj_status_t res = -1;
	int i;
#define BIND_TRIES 3
#define BIND_DELAY_US 100000

	if (!states) {
		return -1;
	}

	/*
	 * transport_apply gets called for EVERY retrieval of a transport when using realtime.
	 * We need to prevent multiple threads from trying to mess with underlying transports
	 * at the same time.  The container is the only thing we have to lock on.
	 */
	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);
	if (perm_state) {
		ast_sorcery_diff(sorcery, perm_state->transport, transport, &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) {
				perm_state->change_detected = 1;
				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 */
			transport->state = perm_state->state;
			copy_state_to_transport(transport);
			ao2_replace(perm_state->transport, transport);
			return 0;
		}
	}

	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);
		return -1;
	}

	/* Set default port if not present */
	if (!pj_sockaddr_get_port(&temp_state->state->host)) {
		pj_sockaddr_set_port(&temp_state->state->host, (transport->type == AST_TRANSPORT_TLS) ? 5061 : 5060);
	}

	/* Now that we know what address family we can set up a dnsmgr refresh for the external media address if present */
	if (!ast_strlen_zero(transport->external_signaling_address)) {
		if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
			temp_state->state->external_address.ss.ss_family = AF_INET;
		} else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) {
			temp_state->state->external_address.ss.ss_family = AF_INET6;
		} else {
			ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external signaling address\n",
					transport_id);
			return -1;
		}

		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);
			return -1;
		}
	}

	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()) {
				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()) {
				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)) {
			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;

		pjsip_tcp_transport_cfg_default(&cfg, temp_state->state->host.addr.sa_family);
		cfg.bind_addr = temp_state->state->host;
		cfg.async_cnt = transport->async_operations;
		set_qos(transport, &cfg.qos_params);

		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) {
		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_sorcery_object_get_id(obj));
			return -1;
		}

		temp_state->state->tls.password = pj_str((char*)transport->password);
		set_qos(transport, &temp_state->state->tls.qos_params);

		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)) {
		if (transport->cos || transport->tos) {
			ast_log(LOG_WARNING, "TOS and COS values ignored for websocket transport\n");
		}
		res = PJ_SUCCESS;
	}

	if (res != PJ_SUCCESS) {
		char msg[PJ_ERR_MSG_SIZE];

		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);
		return -1;
	}

	copy_state_to_transport(transport);
	if (perm_state) {
		ao2_unlink_flags(states, perm_state, OBJ_NOLOCK);
	}
	ao2_link_flags(states, temp_state, OBJ_NOLOCK);

	return 0;
}