diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 32f232c62a4f6c9520ae79abd59207cc41d9a41b..5e9d2d26eaec11d9c5110541772f5eb668ffc393 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -2816,6 +2816,9 @@ static const char *referstatus2str(enum referstatus rstatus) static inline void pvt_set_needdestroy(struct sip_pvt *pvt, const char *reason) { + if (pvt->final_destruction_scheduled) { + return; /* This is already scheduled for final destruction, let the scheduler take care of it. */ + } append_history(pvt, "NeedDestroy", "Setting needdestroy because %s", reason); pvt->needdestroy = 1; } @@ -3564,9 +3567,28 @@ static int __sip_autodestruct(const void *data) return 0; } +/*! \brief Schedule final destruction of SIP dialog. This can not be canceled. + * This function is used to keep a dialog around for a period of time in order + * to properly respond to any retransmits. */ +void sip_scheddestroy_final(struct sip_pvt *p, int ms) +{ + if (p->final_destruction_scheduled) { + return; /* already set final destruction */ + } + + sip_scheddestroy(p, ms); + if (p->autokillid != -1) { + p->final_destruction_scheduled = 1; + } +} + /*! \brief Schedule destruction of SIP dialog */ void sip_scheddestroy(struct sip_pvt *p, int ms) { + if (p->final_destruction_scheduled) { + return; /* already set final destruction */ + } + if (ms < 0) { if (p->timer_t1 == 0) { p->timer_t1 = global_t1; /* Set timer T1 if not set (RFC 3261) */ @@ -3596,6 +3618,11 @@ void sip_scheddestroy(struct sip_pvt *p, int ms) int sip_cancel_destroy(struct sip_pvt *p) { int res = 0; + + if (p->final_destruction_scheduled) { + return res; + } + if (p->autokillid > -1) { int res3; @@ -5675,8 +5702,6 @@ static int sip_hangup(struct ast_channel *ast) update_call_counter(p, DEC_CALL_LIMIT); } ast_debug(4, "SIP Transfer: Not hanging up right now... Rescheduling hangup for %s.\n", p->callid); - if (p->autokillid > -1 && sip_cancel_destroy(p)) - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Really hang up next time */ p->needdestroy = 0; @@ -13006,8 +13031,6 @@ static int cb_extensionstate(char *context, char* exten, int state, void *data) switch(state) { case AST_EXTENSION_DEACTIVATED: /* Retry after a while */ case AST_EXTENSION_REMOVED: /* Extension is gone */ - if (p->autokillid > -1 && sip_cancel_destroy(p)) /* Remove subscription expiry for renewals */ - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); /* Delete subscription in 32 secs */ ast_verb(2, "Extension state: Watcher for hint %s %s. Notify User %s\n", exten, state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", p->username); p->stateid = -1; @@ -21976,9 +21999,10 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req) } else if (p->owner) { ast_set_hangupsource(p->owner, p->owner->name, 0); ast_queue_hangup(p->owner); + sip_scheddestroy_final(p, DEFAULT_TRANS_TIMEOUT); ast_debug(3, "Received bye, issuing owner hangup\n"); } else { - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); + sip_scheddestroy_final(p, DEFAULT_TRANS_TIMEOUT); ast_debug(3, "Received bye, no owner, selfdestruct soon.\n"); } ast_clear_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); diff --git a/channels/sip/include/dialog.h b/channels/sip/include/dialog.h index 554aa5e6a66177627182426aad25da2f2d782472..8972c02d97e4abed9dfde819846f3143be9cb00c 100644 --- a/channels/sip/include/dialog.h +++ b/channels/sip/include/dialog.h @@ -36,6 +36,7 @@ struct sip_pvt *dialog_unref_debug(struct sip_pvt *p, char *tag, char *file, int struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *sin, int useglobal_nat, const int intended_method, struct sip_request *req); +void sip_scheddestroy_final(struct sip_pvt *p, int ms); void sip_scheddestroy(struct sip_pvt *p, int ms); int sip_cancel_destroy(struct sip_pvt *p); diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h index 8d6d0abcb88badf56a708ad2e84f7ab684a6c2cd..95ad34e317e91924326de62e2f63e047f078fac0 100644 --- a/channels/sip/include/sip.h +++ b/channels/sip/include/sip.h @@ -952,8 +952,10 @@ struct sip_pvt { /* boolean flags that don't belong in flags */ unsigned short do_history:1; /*!< Set if we want to record history */ - unsigned short alreadygone:1; /*!< already destroyed by our peer */ - unsigned short needdestroy:1; /*!< need to be destroyed by the monitor thread */ + unsigned short alreadygone:1; /*!< the peer has sent a message indicating termination of the dialog */ + unsigned short needdestroy:1; /*!< this dialog needs to be destroyed by the monitor thread */ + unsigned short final_destruction_scheduled:1; /*!< final dialog destruction is scheduled. Keep dialog + * around until then to handle retransmits. */ unsigned short outgoing_call:1; /*!< this is an outgoing call */ unsigned short answered_elsewhere:1; /*!< This call is cancelled due to answer on another channel */ unsigned short novideo:1; /*!< Didn't get video in invite, don't offer */