Exemple #1
0
/*! \brief Function which applies a negotiated stream */
static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
				       const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream,
				       const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream)
{
	RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free);
	char host[NI_MAXHOST];
	struct t38_state *state;

	if (!session_media->udptl) {
		return 0;
	}

	if (!(state = t38_state_get_or_alloc(session))) {
		return -1;
	}

	ast_copy_pj_str(host, remote_stream->conn ? &remote_stream->conn->addr : &remote->conn->addr, sizeof(host));

	/* Ensure that the address provided is valid */
	if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) {
		/* The provided host was actually invalid so we error out this negotiation */
		return -1;
	}

	ast_sockaddr_set_port(addrs, remote_stream->desc.port);
	ast_udptl_set_peer(session_media->udptl, addrs);

	t38_interpret_sdp(state, session, session_media, remote_stream);

	return 0;
}
Exemple #2
0
/*! \brief Function which defers an incoming media stream */
static enum ast_sip_session_sdp_stream_defer defer_incoming_sdp_stream(
	struct ast_sip_session *session, struct ast_sip_session_media *session_media,
	const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream)
{
	struct t38_state *state;

	if (!session->endpoint->media.t38.enabled) {
		return AST_SIP_SESSION_SDP_DEFER_NOT_HANDLED;
	}

	if (t38_initialize_session(session, session_media)) {
		return AST_SIP_SESSION_SDP_DEFER_ERROR;
	}

	if (!(state = t38_state_get_or_alloc(session))) {
		return AST_SIP_SESSION_SDP_DEFER_ERROR;
	}

	t38_interpret_sdp(state, session, session_media, stream);

	/* If they are initiating the re-invite we need to defer responding until later */
	if (session->t38state == T38_DISABLED) {
		t38_change_state(session, session_media, state, T38_PEER_REINVITE);
		return AST_SIP_SESSION_SDP_DEFER_NEEDED;
	}

	return AST_SIP_SESSION_SDP_DEFER_NOT_NEEDED;
}
/*! \brief Function which negotiates an incoming media stream */
static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
					 const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream)
{
	struct t38_state *state;
	char host[NI_MAXHOST];
	RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free);

	if (!session->endpoint->media.t38.enabled) {
		ast_debug(3, "Declining; T.38 not enabled on session\n");
		return -1;
	}

	if (!(state = t38_state_get_or_alloc(session))) {
		return -1;
	}

	if ((session->t38state == T38_REJECTED) || (session->t38state == T38_DISABLED)) {
		ast_debug(3, "Declining; T.38 state is rejected or declined\n");
		t38_change_state(session, session_media, state, T38_DISABLED);
		return -1;
	}

	ast_copy_pj_str(host, stream->conn ? &stream->conn->addr : &sdp->conn->addr, sizeof(host));

	/* Ensure that the address provided is valid */
	if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_INET) <= 0) {
		/* The provided host was actually invalid so we error out this negotiation */
		ast_debug(3, "Declining; provided host is invalid\n");
		return -1;
	}

	/* Check the address family to make sure it matches configured */
	if ((ast_sockaddr_is_ipv6(addrs) && !session->endpoint->media.t38.ipv6) ||
		(ast_sockaddr_is_ipv4(addrs) && session->endpoint->media.t38.ipv6)) {
		/* The address does not match configured */
		ast_debug(3, "Declining, provided host does not match configured address family\n");
		return -1;
	}

	return 1;
}
Exemple #4
0
/*! \brief Callback for when a response is received for a T.38 re-invite */
static int t38_reinvite_response_cb(struct ast_sip_session *session, pjsip_rx_data *rdata)
{
	struct pjsip_status_line status = rdata->msg_info.msg->line.status;
	struct t38_state *state;
	RAII_VAR(struct ast_sip_session_media *, session_media, NULL, ao2_cleanup);

	if (status.code == 100) {
		return 0;
	}

	if (!(state = t38_state_get_or_alloc(session)) ||
		!(session_media = ao2_find(session->media, "image", OBJ_KEY))) {
		ast_log(LOG_WARNING, "Received response to T.38 re-invite on '%s' but state unavailable\n",
			ast_channel_name(session->channel));
		return 0;
	}

	t38_change_state(session, session_media, state, (status.code == 200) ? T38_ENABLED : T38_REJECTED);

	return 0;
}
Exemple #5
0
/*! \brief Function which creates an outgoing stream */
static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
				      struct pjmedia_sdp_session *sdp)
{
	pj_pool_t *pool = session->inv_session->pool_prov;
	static const pj_str_t STR_IN = { "IN", 2 };
	static const pj_str_t STR_IP4 = { "IP4", 3};
	static const pj_str_t STR_IP6 = { "IP6", 3};
	static const pj_str_t STR_UDPTL = { "udptl", 5 };
	static const pj_str_t STR_T38 = { "t38", 3 };
	static const pj_str_t STR_TRANSFERREDTCF = { "transferredTCF", 14 };
	static const pj_str_t STR_LOCALTCF = { "localTCF", 8 };
	static const pj_str_t STR_T38UDPFEC = { "t38UDPFEC", 9 };
	static const pj_str_t STR_T38UDPREDUNDANCY = { "t38UDPRedundancy", 16 };
	struct t38_state *state;
	pjmedia_sdp_media *media;
	const char *hostip = NULL;
	struct ast_sockaddr addr;
	char tmp[512];
	pj_str_t stmp;

	if (!session->endpoint->media.t38.enabled) {
		return 1;
	} else if ((session->t38state != T38_LOCAL_REINVITE) && (session->t38state != T38_PEER_REINVITE) &&
		(session->t38state != T38_ENABLED)) {
		return 1;
	} else if (!(state = t38_state_get_or_alloc(session))) {
		return -1;
	} else if (t38_initialize_session(session, session_media)) {
		return -1;
	}

	if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media))) ||
		!(media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)))) {
		return -1;
	}

	media->desc.media = pj_str(session_media->stream_type);
	media->desc.transport = STR_UDPTL;

	if (ast_strlen_zero(session->endpoint->media.address)) {
		hostip = ast_sip_get_host_ip_string(session->endpoint->media.t38.ipv6 ? pj_AF_INET6() : pj_AF_INET());
	} else {
		hostip = session->endpoint->media.address;
	}

	if (ast_strlen_zero(hostip)) {
		return -1;
	}

	media->conn->net_type = STR_IN;
	media->conn->addr_type = session->endpoint->media.t38.ipv6 ? STR_IP6 : STR_IP4;
	pj_strdup2(pool, &media->conn->addr, hostip);
	ast_udptl_get_us(session_media->udptl, &addr);
	media->desc.port = (pj_uint16_t) ast_sockaddr_port(&addr);
	media->desc.port_count = 1;
	media->desc.fmt[media->desc.fmt_count++] = STR_T38;

	snprintf(tmp, sizeof(tmp), "%u", state->our_parms.version);
	media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxVersion", pj_cstr(&stmp, tmp));

	snprintf(tmp, sizeof(tmp), "%u", t38_get_rate(state->our_parms.rate));
	media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38MaxBitRate", pj_cstr(&stmp, tmp));

	if (state->our_parms.fill_bit_removal) {
		media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxFillBitRemoval", NULL);
	}

	if (state->our_parms.transcoding_mmr) {
		media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxTranscodingMMR", NULL);
	}

	if (state->our_parms.transcoding_jbig) {
		media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxTranscodingJBIG", NULL);
	}

	switch (state->our_parms.rate_management) {
	case AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF:
		media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxRateManagement", &STR_TRANSFERREDTCF);
		break;
	case AST_T38_RATE_MANAGEMENT_LOCAL_TCF:
		media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxRateManagement", &STR_LOCALTCF);
		break;
	}

	snprintf(tmp, sizeof(tmp), "%u", ast_udptl_get_local_max_datagram(session_media->udptl));
	media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxMaxDatagram", pj_cstr(&stmp, tmp));

	switch (ast_udptl_get_error_correction_scheme(session_media->udptl)) {
	case UDPTL_ERROR_CORRECTION_NONE:
		break;
	case UDPTL_ERROR_CORRECTION_FEC:
		media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxUdpEC", &STR_T38UDPFEC);
		break;
	case UDPTL_ERROR_CORRECTION_REDUNDANCY:
		media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxUdpEC", &STR_T38UDPREDUNDANCY);
		break;
	}

	sdp->media[sdp->media_count++] = media;

	return 1;
}
Exemple #6
0
/*! \brief Task for reacting to T.38 control frame */
static int t38_interpret_parameters(void *obj)
{
	RAII_VAR(struct t38_parameters_task_data *, data, obj, ao2_cleanup);
	const struct ast_control_t38_parameters *parameters = data->frame->data.ptr;
	struct t38_state *state = t38_state_get_or_alloc(data->session);
	RAII_VAR(struct ast_sip_session_media *, session_media, ao2_find(data->session->media, "image", OBJ_KEY), ao2_cleanup);

	/* Without session media or state we can't interpret parameters */
	if (!session_media || !state) {
		return 0;
	}

	switch (parameters->request_response) {
	case AST_T38_NEGOTIATED:
	case AST_T38_REQUEST_NEGOTIATE:         /* Request T38 */
		/* Negotiation can not take place without a valid max_ifp value. */
		if (!parameters->max_ifp) {
			if (data->session->t38state == T38_PEER_REINVITE) {
				t38_change_state(data->session, session_media, state, T38_REJECTED);
				ast_sip_session_resume_reinvite(data->session);
			} else if (data->session->t38state == T38_ENABLED) {
				t38_change_state(data->session, session_media, state, T38_DISABLED);
				ast_sip_session_refresh(data->session, NULL, NULL, NULL,
					AST_SIP_SESSION_REFRESH_METHOD_INVITE, 1);
			}
			break;
		} else if (data->session->t38state == T38_PEER_REINVITE) {
			state->our_parms = *parameters;
			/* modify our parameters to conform to the peer's parameters,
			 * based on the rules in the ITU T.38 recommendation
			 */
			if (!state->their_parms.fill_bit_removal) {
				state->our_parms.fill_bit_removal = 0;
			}
			if (!state->their_parms.transcoding_mmr) {
				state->our_parms.transcoding_mmr = 0;
			}
			if (!state->their_parms.transcoding_jbig) {
				state->our_parms.transcoding_jbig = 0;
			}
			state->our_parms.version = MIN(state->our_parms.version, state->their_parms.version);
			state->our_parms.rate_management = state->their_parms.rate_management;
			ast_udptl_set_local_max_ifp(session_media->udptl, state->our_parms.max_ifp);
			t38_change_state(data->session, session_media, state, T38_ENABLED);
			ast_sip_session_resume_reinvite(data->session);
		} else if (data->session->t38state != T38_ENABLED) {
			if (t38_initialize_session(data->session, session_media)) {
				break;
			}
			state->our_parms = *parameters;
			ast_udptl_set_local_max_ifp(session_media->udptl, state->our_parms.max_ifp);
			t38_change_state(data->session, session_media, state, T38_LOCAL_REINVITE);
			ast_sip_session_refresh(data->session, NULL, t38_reinvite_sdp_cb, t38_reinvite_response_cb,
				AST_SIP_SESSION_REFRESH_METHOD_INVITE, 1);
		}
		break;
	case AST_T38_TERMINATED:
	case AST_T38_REFUSED:
	case AST_T38_REQUEST_TERMINATE:         /* Shutdown T38 */
		if (data->session->t38state == T38_PEER_REINVITE) {
			t38_change_state(data->session, session_media, state, T38_REJECTED);
			ast_sip_session_resume_reinvite(data->session);
		} else if (data->session->t38state == T38_ENABLED) {
			t38_change_state(data->session, session_media, state, T38_DISABLED);
			ast_sip_session_refresh(data->session, NULL, NULL, NULL, AST_SIP_SESSION_REFRESH_METHOD_INVITE, 1);
		}
		break;
	case AST_T38_REQUEST_PARMS: {		/* Application wants remote's parameters re-sent */
		struct ast_control_t38_parameters parameters = state->their_parms;

		if (data->session->t38state == T38_PEER_REINVITE) {
			parameters.max_ifp = ast_udptl_get_far_max_ifp(session_media->udptl);
			parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
			ast_queue_control_data(data->session->channel, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
		}
		break;
	}
	default:
		break;
	}

	return 0;
}