From 1fd3a7849e3a1136f5722221a9f1874a5587b6d6 Mon Sep 17 00:00:00 2001 From: Mark Michelson Date: Wed, 1 Jun 2016 13:48:00 -0500 Subject: ARI: Ensure proper channel state on operations. ARI was recently outfitted with operations to create and dial channels. This leads to the ability to try funny stuff. You could create a channel and then immediately try to play back media on it. You could create a channel, dial it, and while it is ringing attempt to make it continue in the dialplan. This commit attempts to fix this by adding a channel state check to operations that should not be able to operate on outbound channels that have not yet answered. If a channel is in an invalid state, we will send a 412 response. ASTERISK-26047 #close Reported by Mark Michelson Change-Id: I2ca51bf9ef2b44a1dc5a73f2d2de35c62c37dfd8 --- res/ari/resource_channels.c | 115 ++++++++++++++++++++++++++++++++++++++++ res/res_ari_channels.c | 16 ++++++ res/res_ari_recordings.c | 1 + rest-api/api-docs/channels.json | 64 ++++++++++++++++++++++ 4 files changed, 196 insertions(+) diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c index 0f18b2dc1..88e66f11a 100644 --- a/res/ari/resource_channels.c +++ b/res/ari/resource_channels.c @@ -53,6 +53,60 @@ ASTERISK_REGISTER_FILE() #include +/*! + * \brief Ensure channel is in a state that allows operation to be performed. + * + * Since Asterisk 14, it has been possible for down channels, as well as unanswered + * outbound channels to enter Stasis. While some operations are fine to perform on + * such channels, operations that + * + * - Attempt to manipulate channel state + * - Attempt to play media + * - Attempt to control the channel's location in the dialplan + * + * are invalid. This function can be used to determine if the channel is in an + * appropriate state. + * + * \note When this function returns an error, the HTTP response is taken care of. + * + * \param control The app control + * \param response Response to fill in if there is an error + * + * \retval 0 Channel is in a valid state. Continue on! + * \retval non-zero Channel is in an invalid state. Bail! + */ +static int channel_state_invalid(struct stasis_app_control *control, + struct ast_ari_response *response) +{ + struct ast_channel_snapshot *snapshot; + + snapshot = stasis_app_control_get_snapshot(control); + if (!snapshot) { + ast_ari_response_error(response, 404, "Not Found", "Channel not found"); + return -1; + } + + /* These channel states apply only to outbound channels: + * - Down: Channel has been created, and nothing else has been done + * - Reserved: For a PRI, an underlying B-channel is reserved, + * but the channel is not yet dialed + * - Ringing: The channel has been dialed. + * + * This does not affect inbound channels. Inbound channels, when they + * enter the dialplan, are in the "Ring" state. If they have already + * been answered, then they are in the "Up" state. + */ + if (snapshot->state == AST_STATE_DOWN + || snapshot->state == AST_STATE_RESERVED + || snapshot->state == AST_STATE_RINGING) { + ast_ari_response_error(response, 412, "Precondition Failed", + "Channel in invalid state"); + return -1; + } + + return 0; +} + /*! * \brief Finds the control object for a channel, filling the response with an * error, if appropriate. @@ -107,8 +161,13 @@ void ast_ari_channels_continue_in_dialplan( return; } + if (channel_state_invalid(control, response)) { + return; + } + snapshot = stasis_app_control_get_snapshot(control); if (!snapshot) { + ast_ari_response_error(response, 404, "Not Found", "Channel not found"); return; } @@ -175,6 +234,10 @@ void ast_ari_channels_redirect(struct ast_variable *headers, return; } + if (channel_state_invalid(control, response)) { + return; + } + if (ast_strlen_zero(args->endpoint)) { ast_ari_response_error(response, 400, "Not Found", "Required parameter 'endpoint' not provided."); @@ -229,6 +292,10 @@ void ast_ari_channels_answer(struct ast_variable *headers, return; } + if (channel_state_invalid(control, response)) { + return; + } + if (stasis_app_control_answer(control) != 0) { ast_ari_response_error( response, 500, "Internal Server Error", @@ -250,6 +317,10 @@ void ast_ari_channels_ring(struct ast_variable *headers, return; } + if (channel_state_invalid(control, response)) { + return; + } + stasis_app_control_ring(control); ast_ari_response_no_content(response); @@ -266,6 +337,10 @@ void ast_ari_channels_ring_stop(struct ast_variable *headers, return; } + if (channel_state_invalid(control, response)) { + return; + } + stasis_app_control_ring_stop(control); ast_ari_response_no_content(response); @@ -284,6 +359,10 @@ void ast_ari_channels_mute(struct ast_variable *headers, return; } + if (channel_state_invalid(control, response)) { + return; + } + if (ast_strlen_zero(args->direction)) { ast_ari_response_error( response, 400, "Bad Request", @@ -322,6 +401,10 @@ void ast_ari_channels_unmute(struct ast_variable *headers, return; } + if (channel_state_invalid(control, response)) { + return; + } + if (ast_strlen_zero(args->direction)) { ast_ari_response_error( response, 400, "Bad Request", @@ -358,6 +441,10 @@ void ast_ari_channels_send_dtmf(struct ast_variable *headers, return; } + if (channel_state_invalid(control, response)) { + return; + } + if (ast_strlen_zero(args->dtmf)) { ast_ari_response_error( response, 400, "Bad Request", @@ -382,6 +469,10 @@ void ast_ari_channels_hold(struct ast_variable *headers, return; } + if (channel_state_invalid(control, response)) { + return; + } + stasis_app_control_hold(control); ast_ari_response_no_content(response); @@ -399,6 +490,10 @@ void ast_ari_channels_unhold(struct ast_variable *headers, return; } + if (channel_state_invalid(control, response)) { + return; + } + stasis_app_control_unhold(control); ast_ari_response_no_content(response); @@ -416,6 +511,10 @@ void ast_ari_channels_start_moh(struct ast_variable *headers, return; } + if (channel_state_invalid(control, response)) { + return; + } + stasis_app_control_moh_start(control, args->moh_class); ast_ari_response_no_content(response); } @@ -432,6 +531,10 @@ void ast_ari_channels_stop_moh(struct ast_variable *headers, return; } + if (channel_state_invalid(control, response)) { + return; + } + stasis_app_control_moh_stop(control); ast_ari_response_no_content(response); } @@ -448,6 +551,10 @@ void ast_ari_channels_start_silence(struct ast_variable *headers, return; } + if (channel_state_invalid(control, response)) { + return; + } + stasis_app_control_silence_start(control); ast_ari_response_no_content(response); } @@ -464,6 +571,10 @@ void ast_ari_channels_stop_silence(struct ast_variable *headers, return; } + if (channel_state_invalid(control, response)) { + return; + } + stasis_app_control_silence_stop(control); ast_ari_response_no_content(response); } @@ -493,6 +604,10 @@ static void ari_channels_handle_play( return; } + if (channel_state_invalid(control, response)) { + return; + } + snapshot = stasis_app_control_get_snapshot(control); if (!snapshot) { ast_ari_response_error( diff --git a/res/res_ari_channels.c b/res/res_ari_channels.c index 951a5475b..2b7bfe2db 100644 --- a/res/res_ari_channels.c +++ b/res/res_ari_channels.c @@ -811,6 +811,7 @@ static void ast_ari_channels_continue_in_dialplan_cb( case 501: /* Not Implemented */ case 404: /* Channel not found */ case 409: /* Channel not in a Stasis application */ + case 412: /* Channel in invalid state */ is_valid = 1; break; default: @@ -911,6 +912,7 @@ static void ast_ari_channels_redirect_cb( case 404: /* Channel or endpoint not found */ case 409: /* Channel not in a Stasis application */ case 422: /* Endpoint is not the same type as the channel */ + case 412: /* Channel in invalid state */ is_valid = 1; break; default: @@ -971,6 +973,7 @@ static void ast_ari_channels_answer_cb( case 501: /* Not Implemented */ case 404: /* Channel not found */ case 409: /* Channel not in a Stasis application */ + case 412: /* Channel in invalid state */ is_valid = 1; break; default: @@ -1031,6 +1034,7 @@ static void ast_ari_channels_ring_cb( case 501: /* Not Implemented */ case 404: /* Channel not found */ case 409: /* Channel not in a Stasis application */ + case 412: /* Channel in invalid state */ is_valid = 1; break; default: @@ -1091,6 +1095,7 @@ static void ast_ari_channels_ring_stop_cb( case 501: /* Not Implemented */ case 404: /* Channel not found */ case 409: /* Channel not in a Stasis application */ + case 412: /* Channel in invalid state */ is_valid = 1; break; default: @@ -1218,6 +1223,7 @@ static void ast_ari_channels_send_dtmf_cb( case 400: /* DTMF is required */ case 404: /* Channel not found */ case 409: /* Channel not in a Stasis application */ + case 412: /* Channel in invalid state */ is_valid = 1; break; default: @@ -1316,6 +1322,7 @@ static void ast_ari_channels_mute_cb( case 501: /* Not Implemented */ case 404: /* Channel not found */ case 409: /* Channel not in a Stasis application */ + case 412: /* Channel in invalid state */ is_valid = 1; break; default: @@ -1414,6 +1421,7 @@ static void ast_ari_channels_unmute_cb( case 501: /* Not Implemented */ case 404: /* Channel not found */ case 409: /* Channel not in a Stasis application */ + case 412: /* Channel in invalid state */ is_valid = 1; break; default: @@ -1474,6 +1482,7 @@ static void ast_ari_channels_hold_cb( case 501: /* Not Implemented */ case 404: /* Channel not found */ case 409: /* Channel not in a Stasis application */ + case 412: /* Channel in invalid state */ is_valid = 1; break; default: @@ -1534,6 +1543,7 @@ static void ast_ari_channels_unhold_cb( case 501: /* Not Implemented */ case 404: /* Channel not found */ case 409: /* Channel not in a Stasis application */ + case 412: /* Channel in invalid state */ is_valid = 1; break; default: @@ -1632,6 +1642,7 @@ static void ast_ari_channels_start_moh_cb( case 501: /* Not Implemented */ case 404: /* Channel not found */ case 409: /* Channel not in a Stasis application */ + case 412: /* Channel in invalid state */ is_valid = 1; break; default: @@ -1692,6 +1703,7 @@ static void ast_ari_channels_stop_moh_cb( case 501: /* Not Implemented */ case 404: /* Channel not found */ case 409: /* Channel not in a Stasis application */ + case 412: /* Channel in invalid state */ is_valid = 1; break; default: @@ -1752,6 +1764,7 @@ static void ast_ari_channels_start_silence_cb( case 501: /* Not Implemented */ case 404: /* Channel not found */ case 409: /* Channel not in a Stasis application */ + case 412: /* Channel in invalid state */ is_valid = 1; break; default: @@ -1812,6 +1825,7 @@ static void ast_ari_channels_stop_silence_cb( case 501: /* Not Implemented */ case 404: /* Channel not found */ case 409: /* Channel not in a Stasis application */ + case 412: /* Channel in invalid state */ is_valid = 1; break; default: @@ -2003,6 +2017,7 @@ static void ast_ari_channels_play_cb( case 501: /* Not Implemented */ case 404: /* Channel not found */ case 409: /* Channel not in a Stasis application */ + case 412: /* Channel in invalid state */ is_valid = 1; break; default: @@ -2192,6 +2207,7 @@ static void ast_ari_channels_play_with_id_cb( case 501: /* Not Implemented */ case 404: /* Channel not found */ case 409: /* Channel not in a Stasis application */ + case 412: /* Channel in invalid state */ is_valid = 1; break; default: diff --git a/res/res_ari_recordings.c b/res/res_ari_recordings.c index abc264d9e..a21943520 100644 --- a/res/res_ari_recordings.c +++ b/res/res_ari_recordings.c @@ -257,6 +257,7 @@ static void ast_ari_recordings_get_stored_file_cb( break; case 500: /* Internal Server Error */ case 501: /* Not Implemented */ + case 403: /* The recording file could not be opened */ case 404: /* Recording not found */ is_valid = 1; break; diff --git a/rest-api/api-docs/channels.json b/rest-api/api-docs/channels.json index aafd231a1..34436c7b8 100644 --- a/rest-api/api-docs/channels.json +++ b/rest-api/api-docs/channels.json @@ -453,6 +453,10 @@ { "code": 409, "reason": "Channel not in a Stasis application" + }, + { + "code": 412, + "reason": "Channel in invalid state" } ] } @@ -501,6 +505,10 @@ { "code": 422, "reason": "Endpoint is not the same type as the channel" + }, + { + "code": 412, + "reason": "Channel in invalid state" } ] } @@ -533,6 +541,10 @@ { "code": 409, "reason": "Channel not in a Stasis application" + }, + { + "code": 412, + "reason": "Channel in invalid state" } ] } @@ -565,6 +577,10 @@ { "code": 409, "reason": "Channel not in a Stasis application" + }, + { + "code": 412, + "reason": "Channel in invalid state" } ] }, @@ -591,6 +607,10 @@ { "code": 409, "reason": "Channel not in a Stasis application" + }, + { + "code": 412, + "reason": "Channel in invalid state" } ] } @@ -671,6 +691,10 @@ { "code": 409, "reason": "Channel not in a Stasis application" + }, + { + "code": 412, + "reason": "Channel in invalid state" } ] } @@ -720,6 +744,10 @@ { "code": 409, "reason": "Channel not in a Stasis application" + }, + { + "code": 412, + "reason": "Channel in invalid state" } ] }, @@ -763,6 +791,10 @@ { "code": 409, "reason": "Channel not in a Stasis application" + }, + { + "code": 412, + "reason": "Channel in invalid state" } ] } @@ -795,6 +827,10 @@ { "code": 409, "reason": "Channel not in a Stasis application" + }, + { + "code": 412, + "reason": "Channel in invalid state" } ] }, @@ -821,6 +857,10 @@ { "code": 409, "reason": "Channel not in a Stasis application" + }, + { + "code": 412, + "reason": "Channel in invalid state" } ] } @@ -862,6 +902,10 @@ { "code": 409, "reason": "Channel not in a Stasis application" + }, + { + "code": 412, + "reason": "Channel in invalid state" } ] }, @@ -888,6 +932,10 @@ { "code": 409, "reason": "Channel not in a Stasis application" + }, + { + "code": 412, + "reason": "Channel in invalid state" } ] } @@ -921,6 +969,10 @@ { "code": 409, "reason": "Channel not in a Stasis application" + }, + { + "code": 412, + "reason": "Channel in invalid state" } ] }, @@ -947,6 +999,10 @@ { "code": 409, "reason": "Channel not in a Stasis application" + }, + { + "code": 412, + "reason": "Channel in invalid state" } ] } @@ -1021,6 +1077,10 @@ { "code": 409, "reason": "Channel not in a Stasis application" + }, + { + "code": 412, + "reason": "Channel in invalid state" } ] } @@ -1095,6 +1155,10 @@ { "code": 409, "reason": "Channel not in a Stasis application" + }, + { + "code": 412, + "reason": "Channel in invalid state" } ] } -- cgit v1.2.3