From c40496cb388fb1a4d46d687954f3d30c51feeaf8 Mon Sep 17 00:00:00 2001 From: George Joseph Date: Tue, 31 Oct 2023 15:08:14 -0600 Subject: [PATCH] chan_pjsip: Add PJSIPHangup dialplan app and manager action See UserNote below. Exposed the existing Hangup AMI action in manager.c so we can use all of it's channel search and AMI protocol handling without duplicating that code in dialplan_functions.c. Added a lookup function to res_pjsip.c that takes in the string represenation of the pjsip_status_code enum and returns the actual status code. I.E. ast_sip_str2rc("DECLINE") returns 603. This allows the caller to specify PJSIPHangup(decline) in the dialplan, just like Hangup(call_rejected). Also extracted the XML documentation to its own file since it was almost as large as the code itself. UserNote: A new dialplan app PJSIPHangup and AMI action allows you to hang up an unanswered incoming PJSIP call with a specific SIP response code in the 400 -> 699 range. (cherry picked from commit af7e89ebf82f9d23e1f81c7cc6ec20b4ffdf70e6) --- channels/chan_pjsip.c | 14 + channels/pjsip/dialplan_functions.c | 676 ++++---------------- channels/pjsip/dialplan_functions.xml | 659 +++++++++++++++++++ channels/pjsip/include/dialplan_functions.h | 22 +- include/asterisk/manager.h | 41 ++ include/asterisk/res_pjsip.h | 12 + main/manager.c | 18 +- res/res_pjsip.c | 112 ++++ 8 files changed, 992 insertions(+), 562 deletions(-) create mode 100644 channels/pjsip/dialplan_functions.xml diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c index 8582af93ef..18a46ab2dd 100644 --- a/channels/chan_pjsip.c +++ b/channels/chan_pjsip.c @@ -3266,6 +3266,8 @@ static struct ast_custom_function session_refresh_function = { .write = pjsip_acf_session_refresh_write, }; +static char *app_pjsip_hangup = "PJSIPHangup"; + /*! * \brief Load the module * @@ -3323,6 +3325,13 @@ static int load_module(void) goto end; } + if (ast_register_application_xml(app_pjsip_hangup, pjsip_app_hangup)) { + ast_log(LOG_WARNING, "Unable to register PJSIPHangup dialplan application\n"); + goto end; + } + ast_manager_register_xml(app_pjsip_hangup, EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, pjsip_action_hangup); + + ast_sip_register_service(&refer_callback_module); ast_sip_session_register_supplement(&chan_pjsip_supplement); @@ -3370,6 +3379,9 @@ end: ast_custom_function_unregister(&chan_pjsip_dial_contacts_function); ast_custom_function_unregister(&chan_pjsip_parse_uri_function); ast_custom_function_unregister(&session_refresh_function); + ast_unregister_application(app_pjsip_hangup); + ast_manager_unregister(app_pjsip_hangup); + ast_channel_unregister(&chan_pjsip_tech); ast_rtp_glue_unregister(&chan_pjsip_rtp_glue); @@ -3399,6 +3411,8 @@ static int unload_module(void) ast_custom_function_unregister(&chan_pjsip_dial_contacts_function); ast_custom_function_unregister(&chan_pjsip_parse_uri_function); ast_custom_function_unregister(&session_refresh_function); + ast_unregister_application(app_pjsip_hangup); + ast_manager_unregister(app_pjsip_hangup); ast_channel_unregister(&chan_pjsip_tech); ao2_ref(chan_pjsip_tech.capabilities, -1); diff --git a/channels/pjsip/dialplan_functions.c b/channels/pjsip/dialplan_functions.c index 0496f048e3..f1365adba9 100644 --- a/channels/pjsip/dialplan_functions.c +++ b/channels/pjsip/dialplan_functions.c @@ -29,563 +29,6 @@ core ***/ -/*** DOCUMENTATION - - - Return a dial string for dialing all contacts on an AOR. - - - - Name of the endpoint - - - Name of an AOR to use, if not specified the configured AORs on the endpoint are used - - - Optional request user to use in the request URI - - - - Returns a properly formatted dial string for dialing all contacts on an AOR. - - - - - Media and codec offerings to be set on an outbound SIP channel prior to dialing. - - - - types of media offered - - - - When read, returns the codecs offered based upon the media choice. - When written, sets the codecs to offer when an outbound dial attempt is made, - or when a session refresh is sent using PJSIP_SEND_SESSION_REFRESH. - - - - PJSIP_SEND_SESSION_REFRESH - - - - - 13.18.0 - 14.7.0 - 15.1.0 - 16.0.0 - - - Get or change the DTMF mode for a SIP call. - - - - - When read, returns the current DTMF mode - When written, sets the current DTMF mode - This function uses the same DTMF mode naming as the dtmf_mode configuration option - - - - - Get or change the on-hold behavior for a SIP call. - - - - - When read, returns the current moh passthrough mode - When written, sets the current moh passthrough mode - If yes, on-hold re-INVITEs are sent. If no, music on hold is generated. - This function can be used to override the moh_passthrough configuration option - - - - - 13.12.0 - 14.1.0 - 15.0.0 - - - W/O: Initiate a session refresh via an UPDATE or re-INVITE on an established media session - - - - The type of update to send. Default is invite. - - - Send the session refresh as a re-INVITE. - - - Send the session refresh as an UPDATE. - - - - - - This function will cause the PJSIP stack to immediately refresh - the media session for the channel. This will be done using either a - re-INVITE (default) or an UPDATE request. - - This is most useful when combined with the PJSIP_MEDIA_OFFER - dialplan function, as it allows the formats in use on a channel to be - re-negotiated after call setup. - - The formats the endpoint supports are not - checked or enforced by this function. Using this function to offer - formats not supported by the endpoint may result - in a loss of media. - - - ; Within some existing extension on an answered channel - same => n,Set(PJSIP_MEDIA_OFFER(audio)=!all,g722) - same => n,Set(PJSIP_SEND_SESSION_REFRESH()=invite) - - - - PJSIP_MEDIA_OFFER - - - - - 13.24.0 - 16.1.0 - 17.0.0 - - - Parse an uri and return a type part of the URI. - - - - URI to parse - - - The type parameter specifies which URI part to read - - - Display name. - - - URI scheme. - - - User part. - - - Password part. - - - Host part. - - - Port number, or zero. - - - User parameter. - - - Method parameter. - - - Transport parameter. - - - TTL param, or -1. - - - Loose routing param, or zero. - - - Maddr param. - - - - - - Parse an URI and return a specified part of the URI. - - - - - - R/O Retrieve media related information. - - When rtp is specified, the - type parameter must be provided. It specifies - which RTP parameter to read. - - - Retrieve the local address for RTP. - - - Retrieve the remote address for RTP. - - - If direct media is enabled, this address is the remote address - used for RTP. - - - Whether or not the media stream is encrypted. - - - The media stream is not encrypted. - - - The media stream is encrypted. - - - - - Whether or not the media stream is currently restricted - due to a call hold. - - - The media stream is not held. - - - The media stream is held. - - - - - - - When rtp is specified, the - media_type parameter may be provided. It specifies - which media stream the chosen RTP parameter should be retrieved - from. - - - Retrieve information from the audio media stream. - If not specified, audio is used - by default. - - - Retrieve information from the video media stream. - - - - - - R/O Retrieve RTCP statistics. - - When rtcp is specified, the - statistic parameter must be provided. It specifies - which RTCP statistic parameter to read. - - - Retrieve a summary of all RTCP statistics. - The following data items are returned in a semi-colon - delineated list: - - - Our Synchronization Source identifier - - - Their Synchronization Source identifier - - - Our lost packet count - - - Received packet jitter - - - Received packet count - - - Transmitted packet jitter - - - Transmitted packet count - - - Remote lost packet count - - - Round trip time - - - Transmitted Media Experience Score - - - Received Media Experience Score - - - - - Retrieve a summary of all RTCP Jitter statistics. - The following data items are returned in a semi-colon - delineated list: - - - Our minimum jitter - - - Our max jitter - - - Our average jitter - - - Our jitter standard deviation - - - Their minimum jitter - - - Their max jitter - - - Their average jitter - - - Their jitter standard deviation - - - - - Retrieve a summary of all RTCP packet loss statistics. - The following data items are returned in a semi-colon - delineated list: - - - Our minimum lost packets - - - Our max lost packets - - - Our average lost packets - - - Our lost packets standard deviation - - - Their minimum lost packets - - - Their max lost packets - - - Their average lost packets - - - Their lost packets standard deviation - - - - - Retrieve a summary of all RTCP round trip time information. - The following data items are returned in a semi-colon - delineated list: - - - Minimum round trip time - - - Maximum round trip time - - - Average round trip time - - - Standard deviation round trip time - - - - - Retrieve a summary of all RTCP Media Experience Score information. - The following data items are returned in a semi-colon - delineated list: - - - Minimum MES based on us analysing received packets. - - - Maximum MES based on us analysing received packets. - - - Average MES based on us analysing received packets. - - - Standard deviation MES based on us analysing received packets. - - - Minimum MES based on data we get in Sender and Receiver Reports sent by the remote end - - - Maximum MES based on data we get in Sender and Receiver Reports sent by the remote end - - - Average MES based on data we get in Sender and Receiver Reports sent by the remote end - - - Standard deviation MES based on data we get in Sender and Receiver Reports sent by the remote end - - - - Transmitted packet count - Received packet count - Transmitted packet jitter - Received packet jitter - Their max jitter - Their minimum jitter - Their average jitter - Their jitter standard deviation - Our max jitter - Our minimum jitter - Our average jitter - Our jitter standard deviation - Transmitted packet loss - Received packet loss - Their max lost packets - Their minimum lost packets - Their average lost packets - Their lost packets standard deviation - Our max lost packets - Our minimum lost packets - Our average lost packets - Our lost packets standard deviation - Round trip time - Maximum round trip time - Minimum round trip time - Average round trip time - Standard deviation round trip time - Our Synchronization Source identifier - Their Synchronization Source identifier - - Current MES based on us analyzing rtt, jitter and loss - in the actual received RTP stream received from the remote end. - I.E. This is the MES for the incoming audio stream. - - - Current MES based on rtt and the jitter and loss values in - RTCP sender and receiver reports we receive from the - remote end. I.E. This is the MES for the outgoing audio stream. - - Max MES based on data we get in Sender and Receiver Reports sent by the remote end - Min MES based on data we get in Sender and Receiver Reports sent by the remote end - Average MES based on data we get in Sender and Receiver Reports sent by the remote end - Standard deviation MES based on data we get in Sender and Receiver Reports sent by the remote end - Max MES based on us analyzing the received RTP stream - Min MES based on us analyzing the received RTP stream - Average MES based on us analyzing the received RTP stream - Standard deviation MES based on us analyzing the received RTP stream - - - - When rtcp is specified, the - media_type parameter may be provided. It specifies - which media stream the chosen RTCP parameter should be retrieved - from. - - - Retrieve information from the audio media stream. - If not specified, audio is used - by default. - - - Retrieve information from the video media stream. - - - - - - R/O The name of the endpoint associated with this channel. - Use the PJSIP_ENDPOINT function to obtain - further endpoint related information. - - - R/O The name of the contact associated with this channel. - Use the PJSIP_CONTACT function to obtain - further contact related information. Note this may not be present and if so - is only available on outgoing legs. - - - R/O The name of the AOR associated with this channel. - Use the PJSIP_AOR function to obtain - further AOR related information. Note this may not be present and if so - is only available on outgoing legs. - - - R/O Obtain information about the current PJSIP channel and its - session. - - When pjsip is specified, the - type parameter must be provided. It specifies - which signalling parameter to read. - - - The SIP call-id. - - - Whether or not the signalling uses a secure transport. - - The signalling uses a non-secure transport. - The signalling uses a secure transport. - - - - The contact URI where requests are sent. - - - The local URI. - - - Tag in From header - - - The remote URI. - - - Tag in To header - - - The request URI of the incoming INVITE - associated with the creation of this channel. - - - The current state of any T.38 fax on this channel. - - T.38 faxing is disabled on this channel. - Asterisk has sent a re-INVITE to the remote end to initiate a T.38 fax. - The remote end has sent a re-INVITE to Asterisk to initiate a T.38 fax. - A T.38 fax session has been enabled. - A T.38 fax session was attempted but was rejected. - - - - On inbound calls, the full IP address and port number that - the INVITE request was received on. On outbound - calls, the full IP address and port number that the INVITE - request was transmitted from. - - - On inbound calls, the full IP address and port number that - the INVITE request was received from. On outbound - calls, the full IP address and port number that the INVITE - request was transmitted to. - - - - - - - - - ; Log the current Call-ID - same => n,Log(NOTICE, ${CHANNEL(pjsip,call-id)}) - - ; Log the destination address of the audio stream - same => n,Log(NOTICE, ${CHANNEL(rtp,dest)}) - - ; Store the round-trip time associated with a - ; video stream in the CDR field video-rtt - same => n,Set(CDR(video-rtt)=${CHANNEL(rtcp,rtt,video)}) - - -***/ - #include "asterisk.h" #include @@ -596,6 +39,7 @@ #include "asterisk/module.h" #include "asterisk/acl.h" #include "asterisk/app.h" +#include "asterisk/conversions.h" #include "asterisk/channel.h" #include "asterisk/stream.h" #include "asterisk/format.h" @@ -1784,3 +1228,121 @@ int pjsip_acf_session_refresh_write(struct ast_channel *chan, const char *cmd, c return ast_sip_push_task_wait_serializer(channel->session->serializer, refresh_write_cb, &rdata); } + +struct hangup_data { + struct ast_sip_session *session; + int response_code; +}; + +/*! + * \brief Serializer task to hangup channel + */ +static int pjsip_hangup(void *obj) +{ + struct hangup_data *hdata = obj; + pjsip_tx_data *packet = NULL; + + if ((hdata->session->inv_session->state != PJSIP_INV_STATE_DISCONNECTED) && + (pjsip_inv_answer(hdata->session->inv_session, hdata->response_code, NULL, NULL, &packet) == PJ_SUCCESS)) { + ast_sip_session_send_response(hdata->session, packet); + } + + return 0; +} + +/*! + * \brief Callback that validates the response code + */ +static int response_code_validator(const char *channel_name, + const char *response) { + int response_code; + + int rc = ast_str_to_int(response, &response_code); + if (rc != 0) { + response_code = ast_sip_str2rc(response); + if (response_code < 0) { + ast_log(LOG_WARNING, "%s: Unrecognized response code parameter '%s'." + " Defaulting to 603 DECLINE\n", + channel_name, response); + return PJSIP_SC_DECLINE; + } + } + + if (response_code < 400 || response_code > 699) { + ast_log(LOG_WARNING, "%s: Response code %d is out of range 400 -> 699." + " Defaulting to 603 DECLINE\n", + channel_name, response_code); + return PJSIP_SC_DECLINE; + } + return response_code; +} + +/*! + * \brief Called by pjsip_app_hangup and pjsip_action_hangup + * to actually perform the hangup + */ +static void pjsip_app_hangup_handler(struct ast_channel *chan, int response_code) +{ + struct ast_sip_channel_pvt *channel; + struct hangup_data hdata = { NULL, -1 }; + const char *tag = ast_channel_name(chan); + + hdata.response_code = response_code; + + ast_channel_lock(chan); + if (strcmp(ast_channel_tech(chan)->type, "PJSIP")) { + ast_log(LOG_WARNING, "%s: Not a PJSIP channel\n", tag); + ast_channel_unlock(chan); + return; + } + + channel = ast_channel_tech_pvt(chan); + hdata.session = channel->session; + + if (hdata.session->inv_session->role != PJSIP_ROLE_UAS || ( + hdata.session->inv_session->state != PJSIP_INV_STATE_INCOMING && + hdata.session->inv_session->state != PJSIP_INV_STATE_EARLY)) { + ast_log(LOG_WARNING, "%s: Not an incoming channel or invalid state '%s'\n", + tag, pjsip_inv_state_name(hdata.session->inv_session->state)); + ast_channel_unlock(chan); + return; + } + + ast_channel_unlock(chan); + + if (ast_sip_push_task_wait_serializer(channel->session->serializer, + pjsip_hangup, &hdata) != 0) { + ast_log(LOG_WARNING, "%s: failed to push hangup task to serializer\n", tag); + } + + return; +} + +/*! + * \brief PJSIPHangup Dialplan App + */ +int pjsip_app_hangup(struct ast_channel *chan, const char *data) +{ + int response_code; + const char *tag = ast_channel_name(chan); + + if (ast_strlen_zero(data)) { + ast_log(LOG_WARNING, "%s: Missing response code parameter\n", tag); + return -1; + } + + response_code = response_code_validator(tag, data); + + pjsip_app_hangup_handler(chan, response_code); + + return -1; +} + +/*! + * \brief PJSIPHangup Manager Action + */ +int pjsip_action_hangup(struct mansession *s, const struct message *m) +{ + return ast_manager_hangup_helper(s, m, + pjsip_app_hangup_handler, response_code_validator); +} diff --git a/channels/pjsip/dialplan_functions.xml b/channels/pjsip/dialplan_functions.xml new file mode 100644 index 0000000000..be4ce91b96 --- /dev/null +++ b/channels/pjsip/dialplan_functions.xml @@ -0,0 +1,659 @@ + + + + + + Hangup an incoming PJSIP channel with a SIP response code + + + + May be one of... + + A numeric response code in the range 400 ->699 + A response code name from + third-party/pjproject/source/pjsip/include/pjsip/sip_msg.h + such as USE_IDENTITY_HEADER or + PJSIP_SC_USE_IDENTITY_HEADER + + + + + + Hangs up an incoming PJSIP channel and returns the + specified SIP response code in the final response to the caller. + + + + + This function must be called BEFORE anything that + might cause any other final (non 1XX) response to be sent. + For example calling Answer() or + Playback without the + noanswer option will cause the call + to be answered and a final 200 response to be sent. + + + + As with the Hangup application, + the dialplan will terminate after calling this function. + + + The cause code set on the channel will be translated to + a standard ISDN cause code using the table defined in + ast_sip_hangup_sip2cause() in res_pjsip.c + + + + same = n,PJSIPHangup(437) + + + same = n,PJSIPHangup(UNSUPPORTED_CERTIFICATE) + + + same = n,ExecIf($[${SOMEVALUE} = ${SOME_BAD_VALUE}]?PJSIPHangup(437)) + + + + + + + Hangup an incoming PJSIP channel with a SIP response code + + + + + + + + + Hangs up an incoming PJSIP channel and returns the + specified SIP response code in the final response to the caller. + + + + + This function must be called BEFORE anything that + might cause any other final (non 1XX) response to be sent. + For example calling Answer() or + Playback without the + noanswer option will cause the call + to be answered and a final 200 response to be sent. + + + + The cause code set on the channel will be translated to + a standard ISDN cause code using the table defined in + ast_sip_hangup_sip2cause() in res_pjsip.c + + + + Action: PJSIPHangup + ActionID: 12345678 + Channel: PJSIP/alice-00000002 + Cause: 437 + + + Action: PJSIPHangup + ActionID: 12345678 + Channel: PJSIP/alice-00000002 + Cause: UNSUPPORTED_CERTIFICATE + + + + + + + Return a dial string for dialing all contacts on an AOR. + + + + Name of the endpoint + + + Name of an AOR to use, if not specified the configured AORs on the endpoint are used + + + Optional request user to use in the request URI + + + + Returns a properly formatted dial string for dialing all contacts on an AOR. + + + + + Media and codec offerings to be set on an outbound SIP channel prior to dialing. + + + + types of media offered + + + + When read, returns the codecs offered based upon the media choice. + When written, sets the codecs to offer when an outbound dial attempt is made, + or when a session refresh is sent using PJSIP_SEND_SESSION_REFRESH. + + + + PJSIP_SEND_SESSION_REFRESH + + + + + 13.18.0 + 14.7.0 + 15.1.0 + 16.0.0 + + + Get or change the DTMF mode for a SIP call. + + + + + When read, returns the current DTMF mode + When written, sets the current DTMF mode + This function uses the same DTMF mode naming as the dtmf_mode configuration option + + + + + Get or change the on-hold behavior for a SIP call. + + + + + When read, returns the current moh passthrough mode + When written, sets the current moh passthrough mode + If yes, on-hold re-INVITEs are sent. If no, music on hold is generated. + This function can be used to override the moh_passthrough configuration option + + + + + 13.12.0 + 14.1.0 + 15.0.0 + + + W/O: Initiate a session refresh via an UPDATE or re-INVITE on an established media session + + + + The type of update to send. Default is invite. + + + Send the session refresh as a re-INVITE. + + + Send the session refresh as an UPDATE. + + + + + + This function will cause the PJSIP stack to immediately refresh + the media session for the channel. This will be done using either a + re-INVITE (default) or an UPDATE request. + + This is most useful when combined with the PJSIP_MEDIA_OFFER + dialplan function, as it allows the formats in use on a channel to be + re-negotiated after call setup. + + The formats the endpoint supports are not + checked or enforced by this function. Using this function to offer + formats not supported by the endpoint may result + in a loss of media. + + + ; Within some existing extension on an answered channel + same => n,Set(PJSIP_MEDIA_OFFER(audio)=!all,g722) + same => n,Set(PJSIP_SEND_SESSION_REFRESH()=invite) + + + + PJSIP_MEDIA_OFFER + + + + + 13.24.0 + 16.1.0 + 17.0.0 + + + Parse an uri and return a type part of the URI. + + + + URI to parse + + + The type parameter specifies which URI part to read + + + Display name. + + + URI scheme. + + + User part. + + + Password part. + + + Host part. + + + Port number, or zero. + + + User parameter. + + + Method parameter. + + + Transport parameter. + + + TTL param, or -1. + + + Loose routing param, or zero. + + + Maddr param. + + + + + + Parse an URI and return a specified part of the URI. + + + + + + + R/O Retrieve media related information. + + When rtp is specified, the + type parameter must be provided. It specifies + which RTP parameter to read. + + + Retrieve the local address for RTP. + + + Retrieve the remote address for RTP. + + + If direct media is enabled, this address is the remote address + used for RTP. + + + Whether or not the media stream is encrypted. + + + The media stream is not encrypted. + + + The media stream is encrypted. + + + + + Whether or not the media stream is currently restricted + due to a call hold. + + + The media stream is not held. + + + The media stream is held. + + + + + + + When rtp is specified, the + media_type parameter may be provided. It specifies + which media stream the chosen RTP parameter should be retrieved + from. + + + Retrieve information from the audio media stream. + If not specified, audio is used + by default. + + + Retrieve information from the video media stream. + + + + + + R/O Retrieve RTCP statistics. + + When rtcp is specified, the + statistic parameter must be provided. It specifies + which RTCP statistic parameter to read. + + + Retrieve a summary of all RTCP statistics. + The following data items are returned in a semi-colon + delineated list: + + + Our Synchronization Source identifier + + + Their Synchronization Source identifier + + + Our lost packet count + + + Received packet jitter + + + Received packet count + + + Transmitted packet jitter + + + Transmitted packet count + + + Remote lost packet count + + + Round trip time + + + Transmitted Media Experience Score + + + Received Media Experience Score + + + + + Retrieve a summary of all RTCP Jitter statistics. + The following data items are returned in a semi-colon + delineated list: + + + Our minimum jitter + + + Our max jitter + + + Our average jitter + + + Our jitter standard deviation + + + Their minimum jitter + + + Their max jitter + + + Their average jitter + + + Their jitter standard deviation + + + + + Retrieve a summary of all RTCP packet loss statistics. + The following data items are returned in a semi-colon + delineated list: + + + Our minimum lost packets + + + Our max lost packets + + + Our average lost packets + + + Our lost packets standard deviation + + + Their minimum lost packets + + + Their max lost packets + + + Their average lost packets + + + Their lost packets standard deviation + + + + + Retrieve a summary of all RTCP round trip time information. + The following data items are returned in a semi-colon + delineated list: + + + Minimum round trip time + + + Maximum round trip time + + + Average round trip time + + + Standard deviation round trip time + + + + + Retrieve a summary of all RTCP Media Experience Score information. + The following data items are returned in a semi-colon + delineated list: + + + Minimum MES based on us analysing received packets. + + + Maximum MES based on us analysing received packets. + + + Average MES based on us analysing received packets. + + + Standard deviation MES based on us analysing received packets. + + + Minimum MES based on data we get in Sender and Receiver Reports sent by the remote end + + + Maximum MES based on data we get in Sender and Receiver Reports sent by the remote end + + + Average MES based on data we get in Sender and Receiver Reports sent by the remote end + + + Standard deviation MES based on data we get in Sender and Receiver Reports sent by the remote end + + + + Transmitted packet count + Received packet count + Transmitted packet jitter + Received packet jitter + Their max jitter + Their minimum jitter + Their average jitter + Their jitter standard deviation + Our max jitter + Our minimum jitter + Our average jitter + Our jitter standard deviation + Transmitted packet loss + Received packet loss + Their max lost packets + Their minimum lost packets + Their average lost packets + Their lost packets standard deviation + Our max lost packets + Our minimum lost packets + Our average lost packets + Our lost packets standard deviation + Round trip time + Maximum round trip time + Minimum round trip time + Average round trip time + Standard deviation round trip time + Our Synchronization Source identifier + Their Synchronization Source identifier + + Current MES based on us analyzing rtt, jitter and loss + in the actual received RTP stream received from the remote end. + I.E. This is the MES for the incoming audio stream. + + + Current MES based on rtt and the jitter and loss values in + RTCP sender and receiver reports we receive from the + remote end. I.E. This is the MES for the outgoing audio stream. + + Max MES based on data we get in Sender and Receiver Reports sent by the remote end + Min MES based on data we get in Sender and Receiver Reports sent by the remote end + Average MES based on data we get in Sender and Receiver Reports sent by the remote end + Standard deviation MES based on data we get in Sender and Receiver Reports sent by the remote end + Max MES based on us analyzing the received RTP stream + Min MES based on us analyzing the received RTP stream + Average MES based on us analyzing the received RTP stream + Standard deviation MES based on us analyzing the received RTP stream + + + + When rtcp is specified, the + media_type parameter may be provided. It specifies + which media stream the chosen RTCP parameter should be retrieved + from. + + + Retrieve information from the audio media stream. + If not specified, audio is used + by default. + + + Retrieve information from the video media stream. + + + + + + R/O The name of the endpoint associated with this channel. + Use the PJSIP_ENDPOINT function to obtain + further endpoint related information. + + + R/O The name of the contact associated with this channel. + Use the PJSIP_CONTACT function to obtain + further contact related information. Note this may not be present and if so + is only available on outgoing legs. + + + R/O The name of the AOR associated with this channel. + Use the PJSIP_AOR function to obtain + further AOR related information. Note this may not be present and if so + is only available on outgoing legs. + + + R/O Obtain information about the current PJSIP channel and its + session. + + When pjsip is specified, the + type parameter must be provided. It specifies + which signalling parameter to read. + + + The SIP call-id. + + + Whether or not the signalling uses a secure transport. + + The signalling uses a non-secure transport. + The signalling uses a secure transport. + + + + The contact URI where requests are sent. + + + The local URI. + + + Tag in From header + + + The remote URI. + + + Tag in To header + + + The request URI of the incoming INVITE + associated with the creation of this channel. + + + The current state of any T.38 fax on this channel. + + T.38 faxing is disabled on this channel. + Asterisk has sent a re-INVITE to the remote end to initiate a T.38 fax. + The remote end has sent a re-INVITE to Asterisk to initiate a T.38 fax. + A T.38 fax session has been enabled. + A T.38 fax session was attempted but was rejected. + + + + On inbound calls, the full IP address and port number that + the INVITE request was received on. On outbound + calls, the full IP address and port number that the INVITE + request was transmitted from. + + + On inbound calls, the full IP address and port number that + the INVITE request was received from. On outbound + calls, the full IP address and port number that the INVITE + request was transmitted to. + + + + + + + + + ; Log the current Call-ID + same => n,Log(NOTICE, ${CHANNEL(pjsip,call-id)}) + + ; Log the destination address of the audio stream + same => n,Log(NOTICE, ${CHANNEL(rtp,dest)}) + + ; Store the round-trip time associated with a + ; video stream in the CDR field video-rtt + same => n,Set(CDR(video-rtt)=${CHANNEL(rtcp,rtt,video)}) + + + \ No newline at end of file diff --git a/channels/pjsip/include/dialplan_functions.h b/channels/pjsip/include/dialplan_functions.h index d0bf130fe8..03005d5d8f 100644 --- a/channels/pjsip/include/dialplan_functions.h +++ b/channels/pjsip/include/dialplan_functions.h @@ -148,4 +148,24 @@ int pjsip_acf_dial_contacts_read(struct ast_channel *chan, const char *cmd, char */ int pjsip_acf_parse_uri_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len); -#endif /* _PJSIP_DIALPLAN_FUNCTIONS */ \ No newline at end of file +/*! + * \brief Hang up an incoming PJSIP channel with a SIP response code + * \param chan The channel the function is called on + * \param data SIP response code or name + * + * \retval 0 on success + * \retval -1 on failure + */ +int pjsip_app_hangup(struct ast_channel *chan, const char *data); + +/*! + * \brief Manager action to hang up an incoming PJSIP channel with a SIP response code + * \param s session + * \param m message + * + * \retval 0 on success + * \retval -1 on failure + */ +int pjsip_action_hangup(struct mansession *s, const struct message *m); + +#endif /* _PJSIP_DIALPLAN_FUNCTIONS */ diff --git a/include/asterisk/manager.h b/include/asterisk/manager.h index b531956bbf..923f1738cd 100644 --- a/include/asterisk/manager.h +++ b/include/asterisk/manager.h @@ -621,4 +621,45 @@ void ast_manager_publish_event(const char *type, int class_type, struct ast_json */ struct stasis_message_router *ast_manager_get_message_router(void); +/*! + * \brief Callback used by ast_manager_hangup_helper + * that will actually hangup a channel + * + * \param chan The channel to hang up + * \param causecode Cause code to set on the channel + */ +typedef void (*manager_hangup_handler_t)(struct ast_channel *chan, int causecode); + +/*! + * \brief Callback used by ast_manager_hangup_helper + * that will validate the cause code. + + * \param channel_name Mostly for displaying log messages + * \param cause Cause code string + * + * \returns integer cause code + */ +typedef int (*manager_hangup_cause_validator_t)(const char *channel_name, + const char *cause); + +/*! + * \brief A manager helper function that hangs up a channel using a supplied + * channel type specific hangup function and cause code validator + * + * This function handles the lookup of channel(s) and the AMI interaction + * but uses the supplied callbacks to actually perform the hangup. It can be + * used to implement a custom AMI 'Hangup' action without having to duplicate + * all the code in the standard Hangup action. + * + * \param s Session + * \param m Message + * \param handler Function that actually performs the hangup + * \param cause_validator Function that validates the cause code + * + * \retval 0 on success. + * \retval non-zero on error. + */ +int ast_manager_hangup_helper(struct mansession *s, const struct message *m, + manager_hangup_handler_t handler, manager_hangup_cause_validator_t cause_validator); + #endif /* _ASTERISK_MANAGER_H */ diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index 0f68a9bbcb..949f66fdc5 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -4212,5 +4212,17 @@ unsigned int ast_sip_get_all_codecs_on_empty_reinvite(void); */ const int ast_sip_hangup_sip2cause(int cause); +/*! + * \brief Convert name to SIP response code + * + * \param name SIP response code name matching one of the + * enum names defined in "enum pjsip_status_code" + * defined in sip_msg.h. May be specified with or + * without the PJSIP_SC_ prefix. + * + * \retval SIP response code + * \retval -1 if matching code not found + */ +int ast_sip_str2rc(const char *name); #endif /* _RES_PJSIP_H */ diff --git a/main/manager.c b/main/manager.c index 47ccaa6e8f..490ac116e8 100644 --- a/main/manager.c +++ b/main/manager.c @@ -4649,7 +4649,9 @@ static int action_challenge(struct mansession *s, const struct message *m) return 0; } -static int action_hangup(struct mansession *s, const struct message *m) +int ast_manager_hangup_helper(struct mansession *s, + const struct message *m, manager_hangup_handler_t hangup_handler, + manager_hangup_cause_validator_t cause_validator) { struct ast_channel *c = NULL; int causecode = 0; /* all values <= 0 mean 'do not set hangupcause in channel' */ @@ -4673,7 +4675,9 @@ static int action_hangup(struct mansession *s, const struct message *m) idText[0] = '\0'; } - if (!ast_strlen_zero(cause)) { + if (cause_validator) { + causecode = cause_validator(name_or_regex, cause); + } else if (!ast_strlen_zero(cause)) { char *endptr; causecode = strtol(cause, &endptr, 10); if (causecode < 0 || causecode > 127 || *endptr != '\0') { @@ -4700,7 +4704,7 @@ static int action_hangup(struct mansession *s, const struct message *m) ast_sockaddr_stringify_addr(&s->session->addr), ast_channel_name(c)); - ast_channel_softhangup_withcause_locked(c, causecode); + hangup_handler(c, causecode); c = ast_channel_unref(c); astman_send_ack(s, m, "Channel Hungup"); @@ -4746,7 +4750,7 @@ static int action_hangup(struct mansession *s, const struct message *m) ast_sockaddr_stringify_addr(&s->session->addr), ast_channel_name(c)); - ast_channel_softhangup_withcause_locked(c, causecode); + hangup_handler(c, causecode); channels_matched++; astman_append(s, @@ -4766,6 +4770,12 @@ static int action_hangup(struct mansession *s, const struct message *m) return 0; } +static int action_hangup(struct mansession *s, const struct message *m) +{ + return ast_manager_hangup_helper(s, m, + ast_channel_softhangup_withcause_locked, NULL); +} + static int action_setvar(struct mansession *s, const struct message *m) { struct ast_channel *c = NULL; diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 781446eb2a..50bb6575fa 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -3618,6 +3618,118 @@ const int ast_sip_hangup_sip2cause(int cause) return 0; } +struct response_code_map { + int code; + const char *long_name; + const char *short_name; +}; + +/* + * This map was generated from sip_msg.h with + * + * sed -n -r -e 's/^\s+(PJSIP_SC_([^ =]+))\s*=\s*[0-9]+,/{ \1, "\1", "\2" },/gp' \ + * third-party/pjproject/source/pjsip/include/pjsip/sip_msg.h + * + */ +static const struct response_code_map rc_map[] = { + { PJSIP_SC_NULL, "PJSIP_SC_NULL", "NULL" }, + { PJSIP_SC_TRYING, "PJSIP_SC_TRYING", "TRYING" }, + { PJSIP_SC_RINGING, "PJSIP_SC_RINGING", "RINGING" }, + { PJSIP_SC_CALL_BEING_FORWARDED, "PJSIP_SC_CALL_BEING_FORWARDED", "CALL_BEING_FORWARDED" }, + { PJSIP_SC_QUEUED, "PJSIP_SC_QUEUED", "QUEUED" }, + { PJSIP_SC_PROGRESS, "PJSIP_SC_PROGRESS", "PROGRESS" }, + { PJSIP_SC_EARLY_DIALOG_TERMINATED, "PJSIP_SC_EARLY_DIALOG_TERMINATED", "EARLY_DIALOG_TERMINATED" }, + { PJSIP_SC_OK, "PJSIP_SC_OK", "OK" }, + { PJSIP_SC_ACCEPTED, "PJSIP_SC_ACCEPTED", "ACCEPTED" }, + { PJSIP_SC_NO_NOTIFICATION, "PJSIP_SC_NO_NOTIFICATION", "NO_NOTIFICATION" }, + { PJSIP_SC_MULTIPLE_CHOICES, "PJSIP_SC_MULTIPLE_CHOICES", "MULTIPLE_CHOICES" }, + { PJSIP_SC_MOVED_PERMANENTLY, "PJSIP_SC_MOVED_PERMANENTLY", "MOVED_PERMANENTLY" }, + { PJSIP_SC_MOVED_TEMPORARILY, "PJSIP_SC_MOVED_TEMPORARILY", "MOVED_TEMPORARILY" }, + { PJSIP_SC_USE_PROXY, "PJSIP_SC_USE_PROXY", "USE_PROXY" }, + { PJSIP_SC_ALTERNATIVE_SERVICE, "PJSIP_SC_ALTERNATIVE_SERVICE", "ALTERNATIVE_SERVICE" }, + { PJSIP_SC_BAD_REQUEST, "PJSIP_SC_BAD_REQUEST", "BAD_REQUEST" }, + { PJSIP_SC_UNAUTHORIZED, "PJSIP_SC_UNAUTHORIZED", "UNAUTHORIZED" }, + { PJSIP_SC_PAYMENT_REQUIRED, "PJSIP_SC_PAYMENT_REQUIRED", "PAYMENT_REQUIRED" }, + { PJSIP_SC_FORBIDDEN, "PJSIP_SC_FORBIDDEN", "FORBIDDEN" }, + { PJSIP_SC_NOT_FOUND, "PJSIP_SC_NOT_FOUND", "NOT_FOUND" }, + { PJSIP_SC_METHOD_NOT_ALLOWED, "PJSIP_SC_METHOD_NOT_ALLOWED", "METHOD_NOT_ALLOWED" }, + { PJSIP_SC_NOT_ACCEPTABLE, "PJSIP_SC_NOT_ACCEPTABLE", "NOT_ACCEPTABLE" }, + { PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED, "PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED", "PROXY_AUTHENTICATION_REQUIRED" }, + { PJSIP_SC_REQUEST_TIMEOUT, "PJSIP_SC_REQUEST_TIMEOUT", "REQUEST_TIMEOUT" }, + { PJSIP_SC_CONFLICT, "PJSIP_SC_CONFLICT", "CONFLICT" }, + { PJSIP_SC_GONE, "PJSIP_SC_GONE", "GONE" }, + { PJSIP_SC_LENGTH_REQUIRED, "PJSIP_SC_LENGTH_REQUIRED", "LENGTH_REQUIRED" }, + { PJSIP_SC_CONDITIONAL_REQUEST_FAILED, "PJSIP_SC_CONDITIONAL_REQUEST_FAILED", "CONDITIONAL_REQUEST_FAILED" }, + { PJSIP_SC_REQUEST_ENTITY_TOO_LARGE, "PJSIP_SC_REQUEST_ENTITY_TOO_LARGE", "REQUEST_ENTITY_TOO_LARGE" }, + { PJSIP_SC_REQUEST_URI_TOO_LONG, "PJSIP_SC_REQUEST_URI_TOO_LONG", "REQUEST_URI_TOO_LONG" }, + { PJSIP_SC_UNSUPPORTED_MEDIA_TYPE, "PJSIP_SC_UNSUPPORTED_MEDIA_TYPE", "UNSUPPORTED_MEDIA_TYPE" }, + { PJSIP_SC_UNSUPPORTED_URI_SCHEME, "PJSIP_SC_UNSUPPORTED_URI_SCHEME", "UNSUPPORTED_URI_SCHEME" }, + { PJSIP_SC_UNKNOWN_RESOURCE_PRIORITY, "PJSIP_SC_UNKNOWN_RESOURCE_PRIORITY", "UNKNOWN_RESOURCE_PRIORITY" }, + { PJSIP_SC_BAD_EXTENSION, "PJSIP_SC_BAD_EXTENSION", "BAD_EXTENSION" }, + { PJSIP_SC_EXTENSION_REQUIRED, "PJSIP_SC_EXTENSION_REQUIRED", "EXTENSION_REQUIRED" }, + { PJSIP_SC_SESSION_TIMER_TOO_SMALL, "PJSIP_SC_SESSION_TIMER_TOO_SMALL", "SESSION_TIMER_TOO_SMALL" }, + { PJSIP_SC_INTERVAL_TOO_BRIEF, "PJSIP_SC_INTERVAL_TOO_BRIEF", "INTERVAL_TOO_BRIEF" }, + { PJSIP_SC_BAD_LOCATION_INFORMATION, "PJSIP_SC_BAD_LOCATION_INFORMATION", "BAD_LOCATION_INFORMATION" }, + { PJSIP_SC_USE_IDENTITY_HEADER, "PJSIP_SC_USE_IDENTITY_HEADER", "USE_IDENTITY_HEADER" }, + { PJSIP_SC_PROVIDE_REFERRER_HEADER, "PJSIP_SC_PROVIDE_REFERRER_HEADER", "PROVIDE_REFERRER_HEADER" }, + { PJSIP_SC_FLOW_FAILED, "PJSIP_SC_FLOW_FAILED", "FLOW_FAILED" }, + { PJSIP_SC_ANONIMITY_DISALLOWED, "PJSIP_SC_ANONIMITY_DISALLOWED", "ANONIMITY_DISALLOWED" }, + { PJSIP_SC_BAD_IDENTITY_INFO, "PJSIP_SC_BAD_IDENTITY_INFO", "BAD_IDENTITY_INFO" }, + { PJSIP_SC_UNSUPPORTED_CERTIFICATE, "PJSIP_SC_UNSUPPORTED_CERTIFICATE", "UNSUPPORTED_CERTIFICATE" }, + { PJSIP_SC_INVALID_IDENTITY_HEADER, "PJSIP_SC_INVALID_IDENTITY_HEADER", "INVALID_IDENTITY_HEADER" }, + { PJSIP_SC_FIRST_HOP_LACKS_OUTBOUND_SUPPORT, "PJSIP_SC_FIRST_HOP_LACKS_OUTBOUND_SUPPORT", "FIRST_HOP_LACKS_OUTBOUND_SUPPORT" }, + { PJSIP_SC_MAX_BREADTH_EXCEEDED, "PJSIP_SC_MAX_BREADTH_EXCEEDED", "MAX_BREADTH_EXCEEDED" }, + { PJSIP_SC_BAD_INFO_PACKAGE, "PJSIP_SC_BAD_INFO_PACKAGE", "BAD_INFO_PACKAGE" }, + { PJSIP_SC_CONSENT_NEEDED, "PJSIP_SC_CONSENT_NEEDED", "CONSENT_NEEDED" }, + { PJSIP_SC_TEMPORARILY_UNAVAILABLE, "PJSIP_SC_TEMPORARILY_UNAVAILABLE", "TEMPORARILY_UNAVAILABLE" }, + { PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, "PJSIP_SC_CALL_TSX_DOES_NOT_EXIST", "CALL_TSX_DOES_NOT_EXIST" }, + { PJSIP_SC_LOOP_DETECTED, "PJSIP_SC_LOOP_DETECTED", "LOOP_DETECTED" }, + { PJSIP_SC_TOO_MANY_HOPS, "PJSIP_SC_TOO_MANY_HOPS", "TOO_MANY_HOPS" }, + { PJSIP_SC_ADDRESS_INCOMPLETE, "PJSIP_SC_ADDRESS_INCOMPLETE", "ADDRESS_INCOMPLETE" }, + { PJSIP_SC_BUSY_HERE, "PJSIP_SC_BUSY_HERE", "BUSY_HERE" }, + { PJSIP_SC_REQUEST_TERMINATED, "PJSIP_SC_REQUEST_TERMINATED", "REQUEST_TERMINATED" }, + { PJSIP_SC_NOT_ACCEPTABLE_HERE, "PJSIP_SC_NOT_ACCEPTABLE_HERE", "NOT_ACCEPTABLE_HERE" }, + { PJSIP_SC_BAD_EVENT, "PJSIP_SC_BAD_EVENT", "BAD_EVENT" }, + { PJSIP_SC_REQUEST_UPDATED, "PJSIP_SC_REQUEST_UPDATED", "REQUEST_UPDATED" }, + { PJSIP_SC_REQUEST_PENDING, "PJSIP_SC_REQUEST_PENDING", "REQUEST_PENDING" }, + { PJSIP_SC_UNDECIPHERABLE, "PJSIP_SC_UNDECIPHERABLE", "UNDECIPHERABLE" }, + { PJSIP_SC_SECURITY_AGREEMENT_NEEDED, "PJSIP_SC_SECURITY_AGREEMENT_NEEDED", "SECURITY_AGREEMENT_NEEDED" }, + { PJSIP_SC_INTERNAL_SERVER_ERROR, "PJSIP_SC_INTERNAL_SERVER_ERROR", "INTERNAL_SERVER_ERROR" }, + { PJSIP_SC_NOT_IMPLEMENTED, "PJSIP_SC_NOT_IMPLEMENTED", "NOT_IMPLEMENTED" }, + { PJSIP_SC_BAD_GATEWAY, "PJSIP_SC_BAD_GATEWAY", "BAD_GATEWAY" }, + { PJSIP_SC_SERVICE_UNAVAILABLE, "PJSIP_SC_SERVICE_UNAVAILABLE", "SERVICE_UNAVAILABLE" }, + { PJSIP_SC_SERVER_TIMEOUT, "PJSIP_SC_SERVER_TIMEOUT", "SERVER_TIMEOUT" }, + { PJSIP_SC_VERSION_NOT_SUPPORTED, "PJSIP_SC_VERSION_NOT_SUPPORTED", "VERSION_NOT_SUPPORTED" }, + { PJSIP_SC_MESSAGE_TOO_LARGE, "PJSIP_SC_MESSAGE_TOO_LARGE", "MESSAGE_TOO_LARGE" }, + { PJSIP_SC_PUSH_NOTIFICATION_SERVICE_NOT_SUPPORTED, "PJSIP_SC_PUSH_NOTIFICATION_SERVICE_NOT_SUPPORTED", "PUSH_NOTIFICATION_SERVICE_NOT_SUPPORTED" }, + { PJSIP_SC_PRECONDITION_FAILURE, "PJSIP_SC_PRECONDITION_FAILURE", "PRECONDITION_FAILURE" }, + { PJSIP_SC_BUSY_EVERYWHERE, "PJSIP_SC_BUSY_EVERYWHERE", "BUSY_EVERYWHERE" }, + { PJSIP_SC_DECLINE, "PJSIP_SC_DECLINE", "DECLINE" }, + { PJSIP_SC_DOES_NOT_EXIST_ANYWHERE, "PJSIP_SC_DOES_NOT_EXIST_ANYWHERE", "DOES_NOT_EXIST_ANYWHERE" }, + { PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE, "PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE", "NOT_ACCEPTABLE_ANYWHERE" }, + { PJSIP_SC_UNWANTED, "PJSIP_SC_UNWANTED", "UNWANTED" }, + { PJSIP_SC_REJECTED, "PJSIP_SC_REJECTED", "REJECTED" }, +}; + +int ast_sip_str2rc(const char *name) +{ + int i; + + if (ast_strlen_zero(name)) { + return -1; + } + + for (i = 0; i < ARRAY_LEN(rc_map); i++) { + if (strcasecmp(rc_map[i].short_name, name) == 0 || + strcasecmp(rc_map[i].long_name, name) == 0) { + return rc_map[i].code; + } + } + + return -1; +} + + #ifdef TEST_FRAMEWORK AST_TEST_DEFINE(xml_sanitization_end_null) { -- 2.47.2