mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 10:47:18 +00:00 
			
		
		
		
	chan_pjsip: Allow topology/session refreshes in early media state
With this change, session modifications in the early media state are possible if the SDP was sent reliably and confirmed by a PRACK. For details, see RFC 6337, escpecially section 3.2. Resolves: #73
This commit is contained in:
		| @@ -167,6 +167,14 @@ static struct ast_sip_session_supplement chan_pjsip_ack_supplement = { | |||||||
| 	.incoming_request = chan_pjsip_incoming_ack, | 	.incoming_request = chan_pjsip_incoming_ack, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | static int chan_pjsip_incoming_prack(struct ast_sip_session *session, struct pjsip_rx_data *rdata); | ||||||
|  |  | ||||||
|  | static struct ast_sip_session_supplement chan_pjsip_prack_supplement = { | ||||||
|  | 	.method = "PRACK", | ||||||
|  | 	.priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL, | ||||||
|  | 	.incoming_request = chan_pjsip_incoming_prack, | ||||||
|  | }; | ||||||
|  |  | ||||||
| /*! \brief Function called by RTP engine to get local audio RTP peer */ | /*! \brief Function called by RTP engine to get local audio RTP peer */ | ||||||
| static enum ast_rtp_glue_result chan_pjsip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) | static enum ast_rtp_glue_result chan_pjsip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) | ||||||
| { | { | ||||||
| @@ -1564,13 +1572,22 @@ static int send_topology_change_refresh(void *data) | |||||||
| { | { | ||||||
| 	struct topology_change_refresh_data *refresh_data = data; | 	struct topology_change_refresh_data *refresh_data = data; | ||||||
| 	struct ast_sip_session *session = refresh_data->session; | 	struct ast_sip_session *session = refresh_data->session; | ||||||
|  | 	enum ast_channel_state state = ast_channel_state(session->channel); | ||||||
|  | 	enum ast_sip_session_refresh_method method = AST_SIP_SESSION_REFRESH_METHOD_INVITE; | ||||||
| 	int ret; | 	int ret; | ||||||
| 	SCOPE_ENTER(3, "%s: %s\n", ast_sip_session_get_name(session), | 	SCOPE_ENTER(3, "%s: %s\n", ast_sip_session_get_name(session), | ||||||
| 		ast_str_tmp(256, ast_stream_topology_to_str(refresh_data->media_state->topology, &STR_TMP))); | 		ast_str_tmp(256, ast_stream_topology_to_str(refresh_data->media_state->topology, &STR_TMP))); | ||||||
|  |  | ||||||
|  | 	/* See RFC 6337, especially section 3.2: If the early media SDP was sent reliably, we are allowed | ||||||
|  | 	 * to send UPDATEs. Only relevant for AST_STATE_RINGING and AST_STATE_RING - if the channel is UP, | ||||||
|  | 	 * re-INVITES can be sent. | ||||||
|  | 	 */ | ||||||
|  | 	if (session->early_confirmed && (state == AST_STATE_RINGING || state == AST_STATE_RING)) { | ||||||
|  | 		method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	ret = ast_sip_session_refresh(session, NULL, NULL, on_topology_change_response, | 	ret = ast_sip_session_refresh(session, NULL, NULL, on_topology_change_response, | ||||||
| 		AST_SIP_SESSION_REFRESH_METHOD_INVITE, 1, refresh_data->media_state); | 		method, 1, refresh_data->media_state); | ||||||
| 	refresh_data->media_state = NULL; | 	refresh_data->media_state = NULL; | ||||||
| 	topology_change_refresh_data_free(refresh_data); | 	topology_change_refresh_data_free(refresh_data); | ||||||
|  |  | ||||||
| @@ -3141,6 +3158,7 @@ static void chan_pjsip_incoming_response(struct ast_sip_session *session, struct | |||||||
| 		pjsip_rdata_sdp_info *sdp = pjsip_rdata_get_sdp_info(rdata); | 		pjsip_rdata_sdp_info *sdp = pjsip_rdata_get_sdp_info(rdata); | ||||||
| 		if (sdp && sdp->body.ptr) { | 		if (sdp && sdp->body.ptr) { | ||||||
| 			ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session)); | 			ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session)); | ||||||
|  | 			session->early_confirmed = pjsip_100rel_is_reliable(rdata) == PJ_TRUE; | ||||||
| 			ast_queue_control(session->channel, AST_CONTROL_PROGRESS); | 			ast_queue_control(session->channel, AST_CONTROL_PROGRESS); | ||||||
| 		} else { | 		} else { | ||||||
| 			ast_trace(-1, "%s: Queueing RINGING\n", ast_sip_session_get_name(session)); | 			ast_trace(-1, "%s: Queueing RINGING\n", ast_sip_session_get_name(session)); | ||||||
| @@ -3161,6 +3179,7 @@ static void chan_pjsip_incoming_response(struct ast_sip_session *session, struct | |||||||
| 				ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session)); | 				ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session)); | ||||||
| 				ast_trace(1, "%s Method: %.*s Status: %d  Queueing PROGRESS with SDP\n", ast_sip_session_get_name(session), | 				ast_trace(1, "%s Method: %.*s Status: %d  Queueing PROGRESS with SDP\n", ast_sip_session_get_name(session), | ||||||
| 					(int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr, status.code); | 					(int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr, status.code); | ||||||
|  | 				session->early_confirmed = pjsip_100rel_is_reliable(rdata) == PJ_TRUE; | ||||||
| 				ast_queue_control(session->channel, AST_CONTROL_PROGRESS); | 				ast_queue_control(session->channel, AST_CONTROL_PROGRESS); | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| @@ -3195,6 +3214,18 @@ static int chan_pjsip_incoming_ack(struct ast_sip_session *session, struct pjsip | |||||||
| 	SCOPE_EXIT_RTN_VALUE(0, "%s\n", ast_sip_session_get_name(session)); | 	SCOPE_EXIT_RTN_VALUE(0, "%s\n", ast_sip_session_get_name(session)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int chan_pjsip_incoming_prack(struct ast_sip_session *session, struct pjsip_rx_data *rdata) | ||||||
|  | { | ||||||
|  | 	SCOPE_ENTER(3, "%s\n", ast_sip_session_get_name(session)); | ||||||
|  |  | ||||||
|  | 	if (pj_strcmp2(&rdata->msg_info.msg->line.req.method.name, "PRACK") == 0 && | ||||||
|  | 		pjmedia_sdp_neg_get_state(session->inv_session->neg) == PJMEDIA_SDP_NEG_STATE_DONE) { | ||||||
|  |  | ||||||
|  | 		session->early_confirmed = 1; | ||||||
|  | 	} | ||||||
|  | 	SCOPE_EXIT_RTN_VALUE(0, "%s\n", ast_sip_session_get_name(session)); | ||||||
|  | } | ||||||
|  |  | ||||||
| static int update_devstate(void *obj, void *arg, int flags) | static int update_devstate(void *obj, void *arg, int flags) | ||||||
| { | { | ||||||
| 	ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, | 	ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, | ||||||
| @@ -3307,6 +3338,7 @@ static int load_module(void) | |||||||
| 	ast_sip_session_register_supplement(&call_pickup_supplement); | 	ast_sip_session_register_supplement(&call_pickup_supplement); | ||||||
| 	ast_sip_session_register_supplement(&pbx_start_supplement); | 	ast_sip_session_register_supplement(&pbx_start_supplement); | ||||||
| 	ast_sip_session_register_supplement(&chan_pjsip_ack_supplement); | 	ast_sip_session_register_supplement(&chan_pjsip_ack_supplement); | ||||||
|  | 	ast_sip_session_register_supplement(&chan_pjsip_prack_supplement); | ||||||
|  |  | ||||||
| 	if (pjsip_channel_cli_register()) { | 	if (pjsip_channel_cli_register()) { | ||||||
| 		ast_log(LOG_ERROR, "Unable to register PJSIP Channel CLI\n"); | 		ast_log(LOG_ERROR, "Unable to register PJSIP Channel CLI\n"); | ||||||
| @@ -3326,6 +3358,7 @@ end: | |||||||
| 	ao2_cleanup(pjsip_uids_onhold); | 	ao2_cleanup(pjsip_uids_onhold); | ||||||
| 	pjsip_uids_onhold = NULL; | 	pjsip_uids_onhold = NULL; | ||||||
| 	ast_sip_session_unregister_supplement(&chan_pjsip_ack_supplement); | 	ast_sip_session_unregister_supplement(&chan_pjsip_ack_supplement); | ||||||
|  | 	ast_sip_session_unregister_supplement(&chan_pjsip_prack_supplement); | ||||||
| 	ast_sip_session_unregister_supplement(&pbx_start_supplement); | 	ast_sip_session_unregister_supplement(&pbx_start_supplement); | ||||||
| 	ast_sip_session_unregister_supplement(&chan_pjsip_supplement_response); | 	ast_sip_session_unregister_supplement(&chan_pjsip_supplement_response); | ||||||
| 	ast_sip_session_unregister_supplement(&chan_pjsip_supplement); | 	ast_sip_session_unregister_supplement(&chan_pjsip_supplement); | ||||||
| @@ -3355,6 +3388,7 @@ static int unload_module(void) | |||||||
| 	ast_sip_session_unregister_supplement(&chan_pjsip_supplement); | 	ast_sip_session_unregister_supplement(&chan_pjsip_supplement); | ||||||
| 	ast_sip_session_unregister_supplement(&pbx_start_supplement); | 	ast_sip_session_unregister_supplement(&pbx_start_supplement); | ||||||
| 	ast_sip_session_unregister_supplement(&chan_pjsip_ack_supplement); | 	ast_sip_session_unregister_supplement(&chan_pjsip_ack_supplement); | ||||||
|  | 	ast_sip_session_unregister_supplement(&chan_pjsip_prack_supplement); | ||||||
| 	ast_sip_session_unregister_supplement(&call_pickup_supplement); | 	ast_sip_session_unregister_supplement(&call_pickup_supplement); | ||||||
|  |  | ||||||
| 	ast_sip_unregister_service(&refer_callback_module); | 	ast_sip_unregister_service(&refer_callback_module); | ||||||
|   | |||||||
| @@ -231,6 +231,8 @@ struct ast_sip_session { | |||||||
| 	unsigned int ended_while_deferred:1; | 	unsigned int ended_while_deferred:1; | ||||||
| 	/*! Whether to pass through hold and unhold using re-invites with recvonly and sendrecv */ | 	/*! Whether to pass through hold and unhold using re-invites with recvonly and sendrecv */ | ||||||
| 	unsigned int moh_passthrough:1; | 	unsigned int moh_passthrough:1; | ||||||
|  | 	/*! Whether early media state has been confirmed through PRACK */ | ||||||
|  | 	unsigned int early_confirmed:1; | ||||||
| 	/*! DTMF mode to use with this session, from endpoint but can change */ | 	/*! DTMF mode to use with this session, from endpoint but can change */ | ||||||
| 	enum ast_sip_dtmf_mode dtmf; | 	enum ast_sip_dtmf_mode dtmf; | ||||||
| 	/*! Initial incoming INVITE Request-URI.  NULL otherwise. */ | 	/*! Initial incoming INVITE Request-URI.  NULL otherwise. */ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user