diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 3b200e21723235006e145f138959a1e94f9b9c54..4870d0f4a29bca0c9419b1354c1b71e499951821 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1399,6 +1399,7 @@ static char *remove_uri_parameters(char *uri); static int get_refer_info(struct sip_pvt *transferer, struct sip_request *outgoing_req); static int get_also_info(struct sip_pvt *p, struct sip_request *oreq); static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req); +static int use_reason_header(struct sip_pvt *pvt, struct sip_request *req); static int set_address_from_contact(struct sip_pvt *pvt); static void check_via(struct sip_pvt *p, const struct sip_request *req); static int get_rpid(struct sip_pvt *p, struct sip_request *oreq); @@ -16121,6 +16122,49 @@ static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req) return TRUE; } +/*! + * \brief Parses SIP reason header according to RFC3326 and sets channel's hangupcause if configured so + * and header present + * + * \note This is used in BYE and CANCEL request and SIP response, but according to RFC3326 it could + * appear in any request, but makes not a lot of sense in others than BYE or CANCEL. + * Currently only implemented for Q.850 status codes. + * \retval 0 success + * \retval -1 on failure or if not configured + */ +static int use_reason_header(struct sip_pvt *pvt, struct sip_request *req) +{ + int ret, cause; + const char *rp, *rh; + + if (!pvt->owner) { + return -1; + } + + if (!ast_test_flag(&pvt->flags[1], SIP_PAGE2_Q850_REASON) || + !(rh = sip_get_header(req, "Reason"))) { + return -1; + } + + rh = ast_skip_blanks(rh); + if (strncasecmp(rh, "Q.850", 5)) { + return -1; + } + + ret = -1; + cause = ast_channel_hangupcause(pvt->owner); + rp = strstr(rh, "cause="); + if (rp && sscanf(rp + 6, "%3d", &cause) == 1) { + ret = 0; + ast_channel_hangupcause_set(pvt->owner, cause & 0x7f); + if (req->debug) { + ast_verbose("Using Reason header for cause code: %d\n", + ast_channel_hangupcause(pvt->owner)); + } + } + return ret; +} + /*! \brief parse uri in a way that allows semicolon stripping if legacy mode is enabled * * \note This calls parse_uri which has the unexpected property that passing more @@ -24107,27 +24151,13 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc msg = ""; sipmethod = find_sip_method(msg); - owner = p->owner; if (owner) { - const char *rp = NULL, *rh = NULL; - ast_channel_hangupcause_set(owner, 0); - if (ast_test_flag(&p->flags[1], SIP_PAGE2_Q850_REASON) && (rh = sip_get_header(req, "Reason"))) { - rh = ast_skip_blanks(rh); - if (!strncasecmp(rh, "Q.850", 5)) { - int cause = ast_channel_hangupcause(owner); - rp = strstr(rh, "cause="); - if (rp && sscanf(rp + 6, "%30d", &cause) == 1) { - ast_channel_hangupcause_set(owner, cause & 0x7f); - if (req->debug) - ast_verbose("Using Reason header for cause code: %d\n", ast_channel_hangupcause(owner)); - } - } - } - - if (!ast_channel_hangupcause(owner)) + if (use_reason_header(p, req)) { + /* Use the SIP cause */ ast_channel_hangupcause_set(owner, hangup_sip2cause(resp)); + } } if (p->socket.type == AST_TRANSPORT_UDP) { @@ -26419,6 +26449,8 @@ static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req) return 0; } + use_reason_header(p, req); + /* At this point, we could have cancelled the invite at the same time as the other side sends a CANCEL. Our final reply with error code might not have been received by the other side before the CANCEL @@ -26435,7 +26467,7 @@ static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req) stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */ if (p->owner) { - sip_queue_hangup_cause(p, 0); + sip_queue_hangup_cause(p, ast_channel_hangupcause(p->owner)); } else { sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); } @@ -26614,6 +26646,7 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req) stop_session_timer(p); /* Stop Session-Timer */ } + use_reason_header(p, req); if (!ast_strlen_zero(sip_get_header(req, "Also"))) { ast_log(LOG_NOTICE, "Client '%s' using deprecated BYE/Also transfer method. Ask vendor to support REFER instead\n", ast_sockaddr_stringify(&p->recv)); @@ -26654,7 +26687,7 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req) ast_queue_hangup_with_cause(p->owner, AST_CAUSE_PROTOCOL_ERROR); } } else if (p->owner) { - sip_queue_hangup_cause(p, 0); + sip_queue_hangup_cause(p, ast_channel_hangupcause(p->owner)); sip_scheddestroy_final(p, DEFAULT_TRANS_TIMEOUT); ast_debug(3, "Received bye, issuing owner hangup\n"); } else {