mirror of
https://github.com/asterisk/asterisk.git
synced 2025-11-16 14:58:25 +00:00
Merge "chan_sip: Fix early call pickup caused deadlock." into 11
This commit is contained in:
@@ -25121,6 +25121,39 @@ static int sip_t38_abort(const void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Checks state of the p->refer->refer_call state: can it be picked up?
|
||||
*
|
||||
* It will terminate the call if it cannot be picked up; e.g. because it
|
||||
* was gone, or because it wasn't in a ringing state.
|
||||
*
|
||||
* \return 1 if terminated, 0 if ok
|
||||
*/
|
||||
static int terminate_on_invalid_replaces_state(struct sip_pvt *p, struct sip_request *req, const char *replace_id)
|
||||
{
|
||||
if (p->refer->refer_call == p) {
|
||||
ast_log(LOG_NOTICE, "INVITE with replaces into its own call id (%s == %s)!\n", replace_id, p->callid);
|
||||
transmit_response_reliable(p, "400 Bad request", req); /* The best way to not not accept the transfer */
|
||||
|
||||
} else if (!p->refer->refer_call->owner) {
|
||||
/* Oops, someting wrong anyway, no owner, no call */
|
||||
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existing call id (%s)!\n", replace_id);
|
||||
/* Check for better return code */
|
||||
transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replace)", req);
|
||||
|
||||
} else if (ast_channel_state(p->refer->refer_call->owner) != AST_STATE_RINGING &&
|
||||
ast_channel_state(p->refer->refer_call->owner) != AST_STATE_RING &&
|
||||
ast_channel_state(p->refer->refer_call->owner) != AST_STATE_UP) {
|
||||
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-ringing or active call id (%s)!\n", replace_id);
|
||||
transmit_response_reliable(p, "603 Declined (Replaces)", req);
|
||||
|
||||
} else {
|
||||
/* Ok */
|
||||
return 0;
|
||||
}
|
||||
/* Terminated */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief bare-bones support for SIP UPDATE
|
||||
*
|
||||
@@ -25322,6 +25355,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
|
||||
int gotdest;
|
||||
const char *p_replaces;
|
||||
char *replace_id = NULL;
|
||||
int magic_call_id = 0;
|
||||
int refer_locked = 0;
|
||||
const char *required;
|
||||
unsigned int required_profile = 0;
|
||||
@@ -25553,40 +25587,27 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
|
||||
ast_channel_unlock(subscription->owner);
|
||||
}
|
||||
subscription = dialog_unref(subscription, "unref dialog subscription");
|
||||
magic_call_id = !ast_strlen_zero(pickup.exten);
|
||||
}
|
||||
}
|
||||
|
||||
/* This locks both refer_call pvt and refer_call pvt's owner!!!*/
|
||||
if (!error && ast_strlen_zero(pickup.exten) && (p->refer->refer_call = get_sip_pvt_byid_locked(replace_id, totag, fromtag)) == NULL) {
|
||||
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existent call id (%s)!\n", replace_id);
|
||||
transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req);
|
||||
error = 1;
|
||||
} else {
|
||||
refer_locked = 1;
|
||||
}
|
||||
|
||||
/* The matched call is the call from the transferer to Asterisk .
|
||||
We want to bridge the bridged part of the call to the
|
||||
incoming invite, thus taking over the refered call */
|
||||
|
||||
if (p->refer->refer_call == p) {
|
||||
ast_log(LOG_NOTICE, "INVITE with replaces into it's own call id (%s == %s)!\n", replace_id, p->callid);
|
||||
transmit_response_reliable(p, "400 Bad request", req); /* The best way to not not accept the transfer */
|
||||
error = 1;
|
||||
}
|
||||
|
||||
if (!error && ast_strlen_zero(pickup.exten) && !p->refer->refer_call->owner) {
|
||||
/* Oops, someting wrong anyway, no owner, no call */
|
||||
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existing call id (%s)!\n", replace_id);
|
||||
/* Check for better return code */
|
||||
transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replace)", req);
|
||||
error = 1;
|
||||
}
|
||||
|
||||
if (!error && ast_strlen_zero(pickup.exten) && ast_channel_state(p->refer->refer_call->owner) != AST_STATE_RINGING && ast_channel_state(p->refer->refer_call->owner) != AST_STATE_RING && ast_channel_state(p->refer->refer_call->owner) != AST_STATE_UP) {
|
||||
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-ringing or active call id (%s)!\n", replace_id);
|
||||
transmit_response_reliable(p, "603 Declined (Replaces)", req);
|
||||
error = 1;
|
||||
if (!error) {
|
||||
if (magic_call_id) {
|
||||
;
|
||||
/* This locks both refer_call pvt and refer_call pvt's owner!!!*/
|
||||
} else if ((p->refer->refer_call = get_sip_pvt_byid_locked(replace_id, totag, fromtag))) {
|
||||
/* The matched call is the call from the transferer to Asterisk .
|
||||
We want to bridge the bridged part of the call to the
|
||||
incoming invite, thus taking over the refered call */
|
||||
refer_locked = 1;
|
||||
if (terminate_on_invalid_replaces_state(p, req, replace_id)) {
|
||||
error = 1;
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existent call id (%s)!\n", replace_id);
|
||||
transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req);
|
||||
error = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (error) { /* Give up this dialog */
|
||||
@@ -25828,11 +25849,52 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
|
||||
goto request_invite_cleanup;
|
||||
}
|
||||
|
||||
/* We cannot call sip_new with any channel locks
|
||||
* held. Unlock the referred channel if locked. */
|
||||
if (refer_locked) {
|
||||
sip_pvt_unlock(p->refer->refer_call);
|
||||
if (p->refer->refer_call->owner) {
|
||||
ast_channel_unlock(p->refer->refer_call->owner);
|
||||
}
|
||||
}
|
||||
|
||||
/* First invitation - create the channel. Allocation
|
||||
* failures are handled below. */
|
||||
|
||||
c = sip_new(p, AST_STATE_DOWN, S_OR(p->peername, NULL), NULL, p->logger_callid);
|
||||
|
||||
/* Reacquire the lock on the referred call. */
|
||||
if (refer_locked) {
|
||||
/* Reuse deadlock avoid pattern found in
|
||||
* get_sip_pvt_byid_locked. */
|
||||
sip_pvt_lock(p->refer->refer_call);
|
||||
while (p->refer->refer_call->owner && ast_channel_trylock(p->refer->refer_call->owner)) {
|
||||
sip_pvt_unlock(p->refer->refer_call);
|
||||
usleep(1);
|
||||
sip_pvt_lock(p->refer->refer_call);
|
||||
}
|
||||
|
||||
/* And now, check once more that the call is still
|
||||
* still available. Yuck. Bail out if it isn't. */
|
||||
if (terminate_on_invalid_replaces_state(p, req, replace_id)) {
|
||||
/* Free the referred call: it's not ours anymore. */
|
||||
sip_pvt_unlock(p->refer->refer_call);
|
||||
if (p->refer->refer_call->owner) {
|
||||
ast_channel_unlock(p->refer->refer_call->owner);
|
||||
}
|
||||
p->refer->refer_call = dialog_unref(p->refer->refer_call, "unref dialog p->refer->refer_call");
|
||||
refer_locked = 0;
|
||||
/* Kill the channel we just created. */
|
||||
sip_pvt_unlock(p);
|
||||
ast_hangup(c);
|
||||
sip_pvt_lock(p); /* pvt is expected to remain locked on return, so re-lock it */
|
||||
/* Mark the call as failed. */
|
||||
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
|
||||
p->invitestate = INV_COMPLETED;
|
||||
res = INV_REQ_ERROR;
|
||||
goto request_invite_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (cc_recall_core_id != -1) {
|
||||
ast_setup_cc_recall_datastore(c, cc_recall_core_id);
|
||||
ast_cc_agent_set_interfaces_chanvar(c);
|
||||
@@ -25891,7 +25953,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
|
||||
p->lastinvite = seqno;
|
||||
|
||||
if (c && replace_id) { /* Attended transfer or call pickup - we're the target */
|
||||
if (!ast_strlen_zero(pickup.exten)) {
|
||||
if (magic_call_id) {
|
||||
append_history(p, "Xfer", "INVITE/Replace received");
|
||||
|
||||
/* Let the caller know we're giving it a shot */
|
||||
@@ -26062,7 +26124,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
|
||||
|
||||
request_invite_cleanup:
|
||||
|
||||
if (refer_locked && p->refer && p->refer->refer_call) {
|
||||
if (refer_locked) {
|
||||
sip_pvt_unlock(p->refer->refer_call);
|
||||
if (p->refer->refer_call->owner) {
|
||||
ast_channel_unlock(p->refer->refer_call->owner);
|
||||
|
||||
Reference in New Issue
Block a user