Newer
Older
if (option_debug)
ast_log(LOG_DEBUG,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
}
} else if (p->t38.state == T38_DISABLED) { /* Channel doesn't have T38 offered or enabled */
Olle Johansson
committed
int sendok = TRUE;
/* If we are bridged to a channel that has T38 enabled than this is a case of RTP re-invite after T38 session */
/* so handle it here (re-invite other party to RTP) */
struct ast_channel *bridgepeer = NULL;
struct sip_pvt *bridgepvt = NULL;
if ((bridgepeer = ast_bridged_channel(p->owner))) {
if (!strcasecmp(bridgepeer->tech->type, sip_tech.type)) {
bridgepvt = (struct sip_pvt*)bridgepeer->tech_pvt;
Olle Johansson
committed
/* Does the bridged peer have T38 ? */
if (bridgepvt->t38.state == T38_ENABLED) {
ast_log(LOG_WARNING, "RTP re-invite after T38 session not handled yet !\n");
/* Insted of this we should somehow re-invite the other side of the bridge to RTP */
if (ast_test_flag(req, SIP_PKT_IGNORE))
transmit_response(p, "488 Not Acceptable Here (unsupported)", req);
else
transmit_response_reliable(p, "488 Not Acceptable Here (unsupported)", req);
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
Olle Johansson
committed
sendok = FALSE;
}
/* No bridged peer with T38 enabled*/
Olle Johansson
committed
}
if (sendok)
transmit_response_with_sdp(p, "200 OK", req, XMIT_CRITICAL);
Olle Johansson
committed
break;
default:
ast_log(LOG_WARNING, "Don't know how to handle INVITE in state %d\n", c->_state);
transmit_response(p, "100 Trying", req);
break;
if (p && !ast_test_flag(&p->flags[0], SIP_NEEDDESTROY)) {
const char *msg;
if (!p->jointcapability)
msg = "488 Not Acceptable Here (codec error)";
else {
ast_log(LOG_NOTICE, "Unable to create/find SIP channel for this INVITE\n");
if (ast_test_flag(req, SIP_PKT_IGNORE))
transmit_response(p, msg, req);
else
transmit_response_reliable(p, msg, req);
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
}
}
return res;
}
/*! \brief Find all call legs and bridge transferee with target
* called from handle_request_refer */
static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno)
13061
13062
13063
13064
13065
13066
13067
13068
13069
13070
13071
13072
13073
13074
13075
13076
13077
13078
13079
13080
13081
13082
13083
13084
13085
13086
13087
13088
13089
13090
13091
13092
13093
13094
13095
13096
13097
13098
13099
13100
13101
13102
13103
13104
13105
13106
13107
13108
13109
13110
13111
13112
13113
13114
13115
13116
13117
13118
13119
13120
13121
13122
13123
13124
13125
13126
13127
13128
13129
13130
13131
13132
13133
13134
13135
13136
13137
13138
13139
13140
13141
13142
13143
13144
13145
13146
13147
13148
13149
13150
13151
13152
13153
13154
13155
13156
13157
13158
13159
13160
13161
13162
13163
13164
{
struct sip_dual target; /* Chan 1: Call from tranferer to Asterisk */
/* Chan 2: Call from Asterisk to target */
int res = 0;
struct sip_pvt *targetcall_pvt;
int error = 0;
/* Check if the call ID of the replaces header does exist locally */
if (!(targetcall_pvt = get_sip_pvt_byid_locked(transferer->refer->replaces_callid, transferer->refer->replaces_callid_totag,
transferer->refer->replaces_callid_fromtag))) {
if (transferer->refer->localtransfer) {
/* We did not find the refered call. Sorry, can't accept then */
transmit_response(transferer, "202 Accepted", req);
/* Let's fake a response from someone else in order
to follow the standard */
transmit_notify_with_sipfrag(transferer, seqno, "481 Call leg/transaction does not exist", TRUE);
append_history(transferer, "Xfer", "Refer failed");
ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);
transferer->refer->status = REFER_FAILED;
return -1;
}
/* Fall through for remote transfers that we did not find locally */
if (option_debug > 2)
ast_log(LOG_DEBUG, "SIP attended transfer: Not our call - generating INVITE with replaces\n");
return 0;
}
/* Ok, we can accept this transfer */
transmit_response(transferer, "202 Accepted", req);
append_history(transferer, "Xfer", "Refer accepted");
if (!targetcall_pvt->owner) { /* No active channel */
if (option_debug > 3)
ast_log(LOG_DEBUG, "SIP attended transfer: Error: No owner of target call\n");
error = 1;
}
/* We have a channel, find the bridge */
target.chan1 = targetcall_pvt->owner; /* Transferer to Asterisk */
if (!error) {
target.chan2 = ast_bridged_channel(targetcall_pvt->owner); /* Asterisk to target */
if (!target.chan2 || !(target.chan2->_state == AST_STATE_UP || target.chan2->_state == AST_STATE_RINGING) ) {
/* Wrong state of new channel */
if (option_debug > 3) {
if (target.chan2)
ast_log(LOG_DEBUG, "SIP attended transfer: Error: Wrong state of target call: %s\n", ast_state2str(target.chan2->_state));
else if (target.chan1->_state != AST_STATE_RING)
ast_log(LOG_DEBUG, "SIP attended transfer: Error: No target channel\n");
else
ast_log(LOG_DEBUG, "SIP attended transfer: Attempting transfer in ringing state\n");
}
if (target.chan1->_state != AST_STATE_RING)
error = 1;
}
}
if (error) { /* Cancel transfer */
transmit_notify_with_sipfrag(transferer, seqno, "503 Service Unavailable", TRUE);
append_history(transferer, "Xfer", "Refer failed");
ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);
transferer->refer->status = REFER_FAILED;
ast_mutex_unlock(&targetcall_pvt->lock);
ast_channel_unlock(current->chan1);
ast_channel_unlock(target.chan1);
return -1;
}
/* Transfer */
if (option_debug > 3 && sipdebug) {
if (current->chan2) /* We have two bridges */
ast_log(LOG_DEBUG, "SIP attended transfer: trying to bridge %s and %s\n", target.chan1->name, current->chan2->name);
else /* One bridge, propably transfer of IVR/voicemail etc */
ast_log(LOG_DEBUG, "SIP attended transfer: trying to make %s take over (masq) %s\n", target.chan1->name, current->chan1->name);
}
ast_set_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */
/* Perform the transfer */
res = attempt_transfer(current, &target);
ast_mutex_unlock(&targetcall_pvt->lock);
if (res) {
/* Failed transfer */
/* Could find better message, but they will get the point */
transmit_notify_with_sipfrag(transferer, seqno, "486 Busy", TRUE);
append_history(transferer, "Xfer", "Refer failed");
if (targetcall_pvt->owner)
ast_channel_unlock(targetcall_pvt->owner);
/* Right now, we have to hangup, sorry. Bridge is destroyed */
ast_hangup(transferer->owner);
} else {
/* Transfer succeeded! */
/* Tell transferer that we're done. */
transmit_notify_with_sipfrag(transferer, seqno, "200 OK", TRUE);
append_history(transferer, "Xfer", "Refer succeeded");
transferer->refer->status = REFER_200OK;
if (targetcall_pvt->owner) {
ast_log(LOG_DEBUG, "SIP attended transfer: Unlocking channel %s\n", targetcall_pvt->owner->name);
ast_channel_unlock(targetcall_pvt->owner);
}
}
return 1;
}
/*! \brief Handle incoming REFER request */
13166
13167
13168
13169
13170
13171
13172
13173
13174
13175
13176
13177
13178
13179
13180
13181
13182
13183
13184
13185
13186
13187
13188
13189
13190
13191
13192
13193
13194
13195
13196
13197
13198
13199
13200
13201
13202
13203
13204
13205
13206
13207
13208
13209
13210
13211
13212
13213
13214
13215
13216
13217
13218
13219
13220
13221
13222
13223
13224
13225
13226
13227
/*! \page SIP_REFER SIP transfer Support (REFER)
REFER is used for call transfer in SIP. We get a REFER
to place a new call with an INVITE somwhere and then
keep the transferor up-to-date of the transfer. If the
transfer fails, get back on line with the orginal call.
- REFER can be sent outside or inside of a dialog.
Asterisk only accepts REFER inside of a dialog.
- If we get a replaces header, it is an attended transfer
\par Blind transfers
The transferor provides the transferee
with the transfer targets contact. The signalling between
transferer or transferee should not be cancelled, so the
call is recoverable if the transfer target can not be reached
by the transferee.
In this case, Asterisk receives a TRANSFER from
the transferor, thus is the transferee. We should
try to set up a call to the contact provided
and if that fails, re-connect the current session.
If the new call is set up, we issue a hangup.
In this scenario, we are following section 5.2
in the SIP CC Transfer draft. (Transfer without
a GRUU)
\par Transfer with consultation hold
In this case, the transferor
talks to the transfer target before the transfer takes place.
This is implemented with SIP hold and transfer.
Note: The invite From: string could indicate a transfer.
(Section 6. Transfer with consultation hold)
The transferor places the transferee on hold, starts a call
with the transfer target to alert them to the impending
transfer, terminates the connection with the target, then
proceeds with the transfer (as in Blind transfer above)
\par Attended transfer
The transferor places the transferee
on hold, calls the transfer target to alert them,
places the target on hold, then proceeds with the transfer
using a Replaces header field in the Refer-to header. This
will force the transfee to send an Invite to the target,
with a replaces header that instructs the target to
hangup the call between the transferor and the target.
In this case, the Refer/to: uses the AOR address. (The same
URI that the transferee used to establish the session with
the transfer target (To: ). The Require: replaces header should
be in the INVITE to avoid the wrong UA in a forked SIP proxy
scenario to answer and have no call to replace with.
The referred-by header is *NOT* required, but if we get it,
can be copied into the INVITE to the transfer target to
inform the target about the transferor
"Any REFER request has to be appropriately authenticated.".
We can't destroy dialogs, since we want the call to continue.
*/
static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int debug, int ignore, int seqno, int *nounlock)
{
struct sip_dual current; /* Chan1: Call between asterisk and transferer */
/* Chan2: Call between asterisk and transferee */
int res = 0;
if (ast_test_flag(req, SIP_PKT_DEBUG))
ast_verbose("Call %s got a SIP call transfer from %s: (REFER)!\n", p->callid, ast_test_flag(&p->flags[0], SIP_OUTGOING) ? "callee" : "caller");
if (!p->owner) {
/* This is a REFER outside of an existing SIP dialog */
/* We can't handle that, so decline it */
if (option_debug > 2)
ast_log(LOG_DEBUG, "Call %s: Declined REFER, outside of dialog...\n", p->callid);
transmit_response(p, "603 Declined (No dialog)", req);
if (!ast_test_flag(req, SIP_PKT_IGNORE)) {
append_history(p, "Xfer", "Refer failed. Outside of dialog.");
ast_set_flag(&p->flags[0], SIP_ALREADYGONE);
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
}
return 0;
}
13252
13253
13254
13255
13256
13257
13258
13259
13260
13261
13262
13263
13264
13265
13266
13267
13268
13269
13270
13271
13272
13273
13274
13275
13276
13277
13278
13279
13280
13281
13282
13283
13284
13285
13286
13287
13288
13289
13290
13291
13292
13293
13294
13295
13296
13297
13298
13299
13300
13301
13302
13303
13304
13305
/* Check if transfer is allowed from this device */
if (p->allowtransfer == TRANSFER_CLOSED ) {
/* Transfer not allowed, decline */
transmit_response(p, "603 Declined (policy)", req);
append_history(p, "Xfer", "Refer failed. Allowtransfer == closed.");
/* Do not destroy SIP session */
return 0;
}
if(!ignore && ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
/* Already have a pending REFER */
transmit_response(p, "491 Request pending", req);
append_history(p, "Xfer", "Refer failed. Request pending.");
return 0;
}
/* Allocate memory for call transfer data */
if (!p->refer && !sip_refer_allocate(p)) {
transmit_response(p, "500 Internal Server Error", req);
append_history(p, "Xfer", "Refer failed. Memory allocation error.");
return -3;
}
res = get_refer_info(p, req); /* Extract headers */
p->refer->status = REFER_SENT;
if (res != 0) {
switch (res) {
case -2: /* Syntax error */
transmit_response(p, "400 Bad Request (Refer-to missing)", req);
append_history(p, "Xfer", "Refer failed. Refer-to missing.");
if (ast_test_flag(req, SIP_PKT_DEBUG))
ast_log(LOG_DEBUG, "SIP transfer to black hole can't be handled (no refer-to: )\n");
break;
case -3:
transmit_response(p, "603 Declined (Non sip: uri)", req);
append_history(p, "Xfer", "Refer failed. Non SIP uri");
if (ast_test_flag(req, SIP_PKT_DEBUG))
ast_log(LOG_DEBUG, "SIP transfer to non-SIP uri denied\n");
break;
default:
/* Refer-to extension not found, fake a failed transfer */
transmit_response(p, "202 Accepted", req);
append_history(p, "Xfer", "Refer failed. Bad extension.");
transmit_notify_with_sipfrag(p, seqno, "404 Not found", TRUE);
ast_clear_flag(&p->flags[0], SIP_GOTREFER);
if (ast_test_flag(req, SIP_PKT_DEBUG))
ast_log(LOG_DEBUG, "SIP transfer to bad extension: %s\n", p->refer->refer_to);
break;
}
return 0;
}
if (ast_strlen_zero(p->context))
ast_string_field_set(p, context, default_context);
/* If we do not support SIP domains, all transfers are local */
if (allow_external_domains && check_sip_domain(p->refer->refer_to_domain, NULL, 0)) {
p->refer->localtransfer = 1;
if (sipdebug && option_debug > 2)
ast_log(LOG_DEBUG, "This SIP transfer is local : %s\n", p->refer->refer_to_domain);
} else if (AST_LIST_EMPTY(&domain_list)) {
13315
13316
13317
13318
13319
13320
13321
13322
13323
13324
13325
13326
13327
13328
13329
13330
13331
13332
13333
13334
13335
13336
13337
13338
13339
13340
13341
13342
13343
13344
13345
13346
13347
13348
13349
13350
13351
13352
13353
13354
13355
13356
13357
13358
13359
13360
13361
13362
13363
13364
13365
13366
13367
13368
13369
13370
13371
/* This PBX don't bother with SIP domains, so all transfers are local */
p->refer->localtransfer = 1;
} else
if (sipdebug && option_debug > 2)
ast_log(LOG_DEBUG, "This SIP transfer is to a remote SIP extension (remote domain %s)\n", p->refer->refer_to_domain);
/* Is this a repeat of a current request? Ignore it */
/* Don't know what else to do right now. */
if (ignore)
return res;
/* If this is a blind transfer, we have the following
channels to work with:
- chan1, chan2: The current call between transferer and transferee (2 channels)
- target_channel: A new call from the transferee to the target (1 channel)
We need to stay tuned to what happens in order to be able
to bring back the call to the transferer */
/* If this is a attended transfer, we should have all call legs within reach:
- chan1, chan2: The call between the transferer and transferee (2 channels)
- target_channel, targetcall_pvt: The call between the transferer and the target (2 channels)
We want to bridge chan2 with targetcall_pvt!
The replaces call id in the refer message points
to the call leg between Asterisk and the transferer.
So we need to connect the target and the transferee channel
and hangup the two other channels silently
If the target is non-local, the call ID could be on a remote
machine and we need to send an INVITE with replaces to the
target. We basically handle this as a blind transfer
and let the sip_call function catch that we need replaces
header in the INVITE.
*/
/* Get the transferer's channel */
current.chan1 = p->owner;
/* Find the other part of the bridge (2) - transferee */
current.chan2 = ast_bridged_channel(current.chan1);
if (sipdebug && option_debug > 2)
ast_log(LOG_DEBUG, "SIP %s transfer: Transferer channel %s, transferee channel %s\n", p->refer->attendedtransfer ? "attended" : "blind", current.chan1->name, current.chan2 ? current.chan2->name : "<none>");
if (!current.chan2 && !p->refer->attendedtransfer) {
/* No bridged channel, propably IVR or echo or similar... */
/* Guess we should masquerade or something here */
/* Until we figure it out, refuse transfer of such calls */
if (sipdebug && option_debug > 2)
ast_log(LOG_DEBUG,"Refused SIP transfer on non-bridged channel.\n");
p->refer->status = REFER_FAILED;
append_history(p, "Xfer", "Refer failed. Non-bridged channel.");
transmit_response(p, "603 Declined", req);
return -1;
}
Olle Johansson
committed
if (current.chan2) {
if (sipdebug && option_debug > 3)
ast_log(LOG_DEBUG, "Got SIP transfer, applying to bridged peer '%s'\n", current.chan2->name);
Kevin P. Fleming
committed
ast_queue_control(current.chan1, AST_CONTROL_UNHOLD);
Olle Johansson
committed
}
13378
13379
13380
13381
13382
13383
13384
13385
13386
13387
13388
13389
13390
13391
13392
13393
13394
13395
13396
13397
13398
13399
13400
13401
13402
13403
13404
13405
13406
13407
13408
ast_set_flag(&p->flags[0], SIP_GOTREFER);
/* Attended transfer: Find all call legs and bridge transferee with target*/
if (p->refer->attendedtransfer) {
if ((res = local_attended_transfer(p, ¤t, req, seqno)))
return res; /* We're done with the transfer */
/* Fall through for remote transfers that we did not find locally */
if (sipdebug && option_debug > 3)
ast_log(LOG_DEBUG, "SIP attended transfer: Still not our call - generating INVITE with replaces\n");
/* Fallthrough if we can't find the call leg internally */
}
/* Parking a call */
if (p->refer->localtransfer && !strcmp(p->refer->refer_to, ast_parking_ext())) {
/* Must release c's lock now, because it will not longer be accessible after the transfer! */
*nounlock = 1;
ast_channel_unlock(current.chan1);
copy_request(¤t.req, req);
ast_clear_flag(&p->flags[0], SIP_GOTREFER);
p->refer->status = REFER_200OK;
append_history(p, "Xfer", "REFER to call parking.");
if (sipdebug && option_debug > 3)
ast_log(LOG_DEBUG, "SIP transfer to parking: trying to park %s. Parked by %s\n", current.chan2->name, current.chan1->name);
sip_park(current.chan2, current.chan1, req, seqno);
return res;
}
/* Blind transfers and remote attended xfers */
transmit_response(p, "202 Accepted", req);
Olle Johansson
committed
if (current.chan1 && current.chan2) {
ast_log(LOG_DEBUG, "chan1->name: %s\n", current.chan1->name);
pbx_builtin_setvar_helper(current.chan1, "BLINDTRANSFER", current.chan2->name);
}
if (current.chan2) {
Olle Johansson
committed
pbx_builtin_setvar_helper(current.chan2, "BLINDTRANSFER", current.chan1->name);
13417
13418
13419
13420
13421
13422
13423
13424
13425
13426
13427
13428
13429
13430
13431
13432
13433
13434
13435
13436
13437
13438
13439
13440
13441
13442
13443
13444
13445
13446
13447
13448
13449
pbx_builtin_setvar_helper(current.chan2, "SIPDOMAIN", p->refer->refer_to_domain);
pbx_builtin_setvar_helper(current.chan2, "SIPTRANSFER", "yes");
/* One for the new channel */
pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER", "yes");
if (p->refer->referred_by)
pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REFERER", p->refer->referred_by);
if (p->refer->referred_by)
/* Attended transfer to remote host, prepare headers for the INVITE */
pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REFERER", p->refer->referred_by);
}
/* Generate an URI-encoded string */
if (p->refer->replaces_callid && !ast_strlen_zero(p->refer->replaces_callid)) {
char tempheader[BUFSIZ];
char tempheader2[BUFSIZ];
snprintf(tempheader, sizeof(tempheader), "%s%s%s%s%s", p->refer->replaces_callid,
p->refer->replaces_callid_totag ? ";to-tag=" : "",
p->refer->replaces_callid_totag,
p->refer->replaces_callid_fromtag ? ";from-tag=" : "",
p->refer->replaces_callid_fromtag);
/* Convert it to URL encoding, also convert reserved strings */
ast_uri_encode(tempheader, tempheader2, sizeof(tempheader2), 1);
if (current.chan2)
pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REPLACES", tempheader2);
}
/* Must release lock now, because it will not longer
be accessible after the transfer! */
*nounlock = 1;
ast_channel_unlock(current.chan1);
ast_channel_unlock(current.chan2);
/* Connect the call */
Olle Johansson
committed
13451
13452
13453
13454
13455
13456
13457
13458
13459
13460
13461
13462
13463
13464
13465
13466
13467
13468
13469
13470
13471
13472
13473
13474
13475
13476
13477
13478
13479
13480
13481
13482
13483
13484
13485
13486
13487
13488
13489
13490
13491
13492
13493
13494
13495
13496
13497
13498
13499
13500
13501
13502
13503
/* FAKE ringing if not attended transfer */
if (!p->refer->attendedtransfer)
transmit_notify_with_sipfrag(p, seqno, "183 Ringing", FALSE);
/* For blind transfer, this will lead to a new call */
/* For attended transfer to remote host, this will lead to
a new SIP call with a replaces header, if the dial plan allows it
*/
if (!current.chan2) {
/* We have no bridge, so we're talking with Asterisk somehow */
/* We need to masquerade this call */
/* What to do to fix this situation:
* Set up the new call in a new channel
* Let the new channel masq into this channel
Please add that code here :-)
*/
transmit_response(p, "202 Accepted", req);
p->refer->status = REFER_FAILED;
transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable (can't handle one-legged xfers)", TRUE);
ast_clear_flag(&p->flags[0], SIP_GOTREFER);
append_history(p, "Xfer", "Refer failed (only bridged calls).");
return -1;
}
ast_set_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */
/* For blind transfers, move the call to the new extensions. For attended transfers on multiple
servers - generate an INVITE with Replaces. Either way, let the dial plan decided */
res = ast_async_goto(current.chan2, p->refer->refer_to_context, p->refer->refer_to, 1);
if (!res) {
/* Success - we have a new channel */
if (option_debug > 2)
ast_log(LOG_DEBUG, "%s transfer succeeded. Telling transferer.\n", p->refer->attendedtransfer? "Attended" : "Blind");
transmit_notify_with_sipfrag(p, seqno, "200 Ok", TRUE);
if (p->refer->localtransfer)
p->refer->status = REFER_200OK;
if (p->owner)
p->owner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
append_history(p, "Xfer", "Refer succeeded.");
ast_clear_flag(&p->flags[0], SIP_GOTREFER);
/* Do not hangup call, the other side do that when we say 200 OK */
/* We could possibly implement a timer here, auto congestion */
res = 0;
} else {
ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Don't delay hangup */
if (option_debug > 2)
ast_log(LOG_DEBUG, "%s transfer failed. Resuming original call.\n", p->refer->attendedtransfer? "Attended" : "Blind");
append_history(p, "Xfer", "Refer failed.");
/* Failure of some kind */
p->refer->status = REFER_FAILED;
transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable", TRUE);
ast_clear_flag(&p->flags[0], SIP_GOTREFER);
res = -1;
}
return res;
}
/*! \brief Handle incoming CANCEL request */
static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req)
{
check_via(p, req);
ast_set_flag(&p->flags[0], SIP_ALREADYGONE);
if (p->owner && p->owner->_state == AST_STATE_UP) {
/* This call is up, cancel is ignored, we need a bye */
transmit_response(p, "200 OK", req);
if (option_debug)
ast_log(LOG_DEBUG, "Got CANCEL on an answered call. Ignoring... \n");
return 0;
}
if (p->rtp) {
/* Immediately stop RTP */
ast_rtp_stop(p->rtp);
}
if (p->vrtp) {
/* Immediately stop VRTP */
ast_rtp_stop(p->vrtp);
}
if (p->udptl) {
/* Immediately stop UDPTL */
ast_udptl_stop(p->udptl);
}
if (p->owner)
ast_queue_hangup(p->owner);
else
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
if (p->initreq.len > 0) {
transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
transmit_response(p, "200 OK", req);
return 1;
} else {
transmit_response(p, "481 Call Leg Does Not Exist", req);
return 0;
}
}
/*! \brief Handle incoming BYE request */
static int handle_request_bye(struct sip_pvt *p, struct sip_request *req)
{
struct ast_channel *c=NULL;
int res;
struct ast_channel *bridged_to;
char *audioqos = NULL, *videoqos = NULL;
if (p->pendinginvite && !ast_test_flag(&p->flags[0], SIP_OUTGOING) && !ast_test_flag(req, SIP_PKT_IGNORE))
transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
copy_request(&p->initreq, req);
check_via(p, req);
ast_set_flag(&p->flags[0], SIP_ALREADYGONE);
if (p->rtp)
audioqos = ast_rtp_get_quality(p->rtp);
if (p->vrtp)
videoqos = ast_rtp_get_quality(p->vrtp);
/* Get RTCP quality before end of call */
if (recordhistory) {
if (p->rtp)
append_history(p, "RTCPaudio", "Quality:%s", audioqos);
append_history(p, "RTCPvideo", "Quality:%s", videoqos);
if (p->owner)
pbx_builtin_setvar_helper(p->owner, "RTPAUDIOQOS", audioqos);
/* Immediately stop RTP */
ast_rtp_stop(p->rtp);
}
if (p->vrtp) {
if (p->owner)
pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", videoqos);
/* Immediately stop VRTP */
ast_rtp_stop(p->vrtp);
}
if (p->udptl) {
/* Immediately stop UDPTL */
ast_udptl_stop(p->udptl);
}
if (!ast_strlen_zero(get_header(req, "Also"))) {
ast_log(LOG_NOTICE, "Client '%s' using deprecated BYE/Also transfer method. Ask vendor to support REFER instead\n",
Russell Bryant
committed
ast_inet_ntoa(p->recv.sin_addr));
if (ast_strlen_zero(p->context))
ast_string_field_set(p, context, default_context);
res = get_also_info(p, req);
if (!res) {
c = p->owner;
if (c) {
bridged_to = ast_bridged_channel(c);
if (bridged_to) {
/* Don't actually hangup here... */
Kevin P. Fleming
committed
ast_queue_control(c, AST_CONTROL_UNHOLD);
ast_async_goto(bridged_to, p->context, p->refer->refer_to,1);
} else
ast_queue_hangup(p->owner);
}
} else {
Russell Bryant
committed
ast_log(LOG_WARNING, "Invalid transfer information from '%s'\n", ast_inet_ntoa(p->recv.sin_addr));
if (p->owner)
ast_queue_hangup(p->owner);
}
} else if (p->owner)
ast_queue_hangup(p->owner);
else
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
transmit_response(p, "200 OK", req);
return 1;
}
/*! \brief Handle incoming MESSAGE request */
static int handle_request_message(struct sip_pvt *p, struct sip_request *req)
if (!ast_test_flag(req, SIP_PKT_IGNORE)) {
if (ast_test_flag(req, SIP_PKT_DEBUG))
Kevin P. Fleming
committed
ast_verbose("Receiving message!\n");
receive_message(p, req);
}
transmit_response(p, "202 Accepted", req);
return 1;
}
/*! \brief Handle incoming SUBSCRIBE request */
static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e)
{
int gotdest;
int res = 0;
Kevin P. Fleming
committed
int firststate = AST_EXTENSION_REMOVED;
Kevin P. Fleming
committed
struct sip_peer *authpeer = NULL;
const char *event = get_header(req, "Event"); /* Get Event package name */
const char *accept = get_header(req, "Accept");
int resubscribe = (p->subscribed != NONE);
if (p->initreq.headers) {
Kevin P. Fleming
committed
/* We already have a dialog */
if (p->initreq.method != SIP_SUBSCRIBE) {
/* This is a SUBSCRIBE within another SIP dialog, which we do not support */
/* For transfers, this could happen, but since we haven't seen it happening, let us just refuse this */
transmit_response(p, "403 Forbidden (within dialog)", req);
/* Do not destroy session, since we will break the call if we do */
ast_log(LOG_DEBUG, "Got a subscription within the context of another call, can't handle that - %s (Method %s)\n", p->callid, sip_methods[p->initreq.method].text);
return 0;
} else if (ast_test_flag(req, SIP_PKT_DEBUG)) {
Kevin P. Fleming
committed
ast_log(LOG_DEBUG, "Got a re-subscribe on existing subscription %s\n", p->callid);
Kevin P. Fleming
committed
else
ast_log(LOG_DEBUG, "Got a new subscription %s (possibly with auth)\n", p->callid);
Kevin P. Fleming
committed
}
}
/* Check if we have a global disallow setting on subscriptions.
if so, we don't have to check peer/user settings after auth, which saves a lot of processing
*/
if (!global_allowsubscribe) {
transmit_response(p, "403 Forbidden (policy)", req);
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
if (!ast_test_flag(req, SIP_PKT_IGNORE) && !p->initreq.headers) { /* Set up dialog, new subscription */
/* Use this as the basis */
if (ast_test_flag(req, SIP_PKT_DEBUG))
Kevin P. Fleming
committed
ast_verbose("Creating new subscription\n");
/* This call is no longer outgoing if it ever was */
ast_clear_flag(&p->flags[0], SIP_OUTGOING);
copy_request(&p->initreq, req);
check_via(p, req);
} else if (ast_test_flag(req, SIP_PKT_DEBUG) && ast_test_flag(req, SIP_PKT_IGNORE))
ast_verbose("Ignoring this SUBSCRIBE request\n");
/* Find parameters to Event: header value and remove them for now */
event = strsep((char **)&event, ";"); /* XXX bug here, overwrite string */
/* Handle authentication if this is our first subscribe */
res = check_user_full(p, req, SIP_SUBSCRIBE, e, 0, sin, &authpeer);
/* if an authentication response was sent, we are done here */
if (res == AUTH_CHALLENGE_SENT)
return 0;
if (res < 0) {
if (res == AUTH_FAKE_AUTH) {
ast_log(LOG_NOTICE, "Sending fake auth rejection for user %s\n", get_header(req, "From"));
transmit_fake_auth_response(p, req, 1);
} else {
ast_log(LOG_NOTICE, "Failed to authenticate user %s for SUBSCRIBE\n", get_header(req, "From"));
transmit_response_reliable(p, "403 Forbidden", req);
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
return 0;
}
/* Check if this user/peer is allowed to subscribe at all */
if (!ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)) {
transmit_response(p, "403 Forbidden (policy)", req);
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
return 0;
}
/* Get destination right away */
gotdest = get_destination(p, NULL);
/* Initialize the context if it hasn't been already;
note this is done _after_ handling any domain lookups,
because the context specified there is for calls, not
subscriptions
*/
if (!ast_strlen_zero(p->subscribecontext))
ast_string_field_set(p, context, p->subscribecontext);
else if (ast_strlen_zero(p->context))
ast_string_field_set(p, context, default_context);
build_contact(p);
if (gotdest) {
transmit_response(p, "404 Not Found", req);
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
return 0;
} else {
/* XXX reduce nesting here */
13732
13733
13734
13735
13736
13737
13738
13739
13740
13741
13742
13743
13744
13745
13746
13747
13748
13749
13750
13751
13752
/* Initialize tag for new subscriptions */
if (ast_strlen_zero(p->tag))
make_our_tag(p->tag, sizeof(p->tag));
if (!strcmp(event, "presence") || !strcmp(event, "dialog")) { /* Presence, RFC 3842 */
/* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */
if (strstr(accept, "application/pidf+xml")) {
p->subscribed = PIDF_XML; /* RFC 3863 format */
} else if (strstr(accept, "application/dialog-info+xml")) {
p->subscribed = DIALOG_INFO_XML;
/* IETF draft: draft-ietf-sipping-dialog-package-05.txt */
} else if (strstr(accept, "application/cpim-pidf+xml")) {
p->subscribed = CPIM_PIDF_XML; /* RFC 3863 format */
} else if (strstr(accept, "application/xpidf+xml")) {
p->subscribed = XPIDF_XML; /* Early pre-RFC 3863 format with MSN additions (Microsoft Messenger) */
} else if (strstr(p->useragent, "Polycom")) {
p->subscribed = XPIDF_XML; /* Polycoms subscribe for "event: dialog" but don't include an "accept:" header */
} else {
/* Can't find a format for events that we know about */
transmit_response(p, "489 Bad Event", req);
Kevin P. Fleming
committed
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
return 0;
}
} else if (!strcmp(event, "message-summary")) {
if (!ast_strlen_zero(accept) && strcmp(accept, "application/simple-message-summary")) {
/* Format requested that we do not support */
transmit_response(p, "406 Not Acceptable", req);
if (option_debug > 1)
ast_log(LOG_DEBUG, "Received SIP mailbox subscription for unknown format: %s\n", accept);
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
return 0;
}
/* Looks like they actually want a mailbox status
This version of Asterisk supports mailbox subscriptions
The subscribed URI needs to exist in the dial plan
In most devices, this is configurable to the voicemailmain extension you use
*/
if (!authpeer || ast_strlen_zero(authpeer->mailbox)) {
transmit_response(p, "404 Not found (no mailbox)", req);
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
ast_log(LOG_NOTICE, "Received SIP subscribe for peer without mailbox: %s\n", authpeer->name);
Kevin P. Fleming
committed
return 0;
}
p->subscribed = MWI_NOTIFICATION;
if (authpeer->mwipvt && authpeer->mwipvt != p) /* Destroy old PVT if this is a new one */
/* We only allow one subscription per peer */
sip_destroy(authpeer->mwipvt);
authpeer->mwipvt = p; /* Link from peer to pvt */
p->relatedpeer = authpeer; /* Link from pvt to peer */
} else { /* At this point, Asterisk does not understand the specified event */
transmit_response(p, "489 Bad Event", req);
if (option_debug > 1)
ast_log(LOG_DEBUG, "Received SIP subscribe for unknown event package: %s\n", event);
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
return 0;
if (p->subscribed != MWI_NOTIFICATION && !resubscribe)
p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, p);
}
if (!ast_test_flag(req, SIP_PKT_IGNORE) && p)
p->lastinvite = seqno;
if (p && !ast_test_flag(&p->flags[0], SIP_NEEDDESTROY)) {
p->expiry = atoi(get_header(req, "Expires"));
Olle Johansson
committed
/* check if the requested expiry-time is within the approved limits from sip.conf */
if (p->expiry > max_expiry)
p->expiry = max_expiry;
if (p->expiry < min_expiry && p->expiry > 0)
Olle Johansson
committed
p->expiry = min_expiry;
Kevin P. Fleming
committed
if (sipdebug || option_debug > 1) {
if (p->subscribed == MWI_NOTIFICATION && p->relatedpeer)
ast_log(LOG_DEBUG, "Adding subscription for mailbox notification - peer %s Mailbox %s\n", p->relatedpeer->name, p->relatedpeer->mailbox);
else
ast_log(LOG_DEBUG, "Adding subscription for extension %s context %s for peer %s\n", p->exten, p->context, p->username);
}
if (p->autokillid > -1)
sip_cancel_destroy(p); /* Remove subscription expiry for renewals */
if (p->expiry > 0)
sip_scheddestroy(p, (p->expiry + 10) * 1000); /* Set timer for destruction of call at expiration */
Kevin P. Fleming
committed
Kevin P. Fleming
committed
if (p->subscribed == MWI_NOTIFICATION) {
transmit_response(p, "200 OK", req);
Kevin P. Fleming
committed
if (p->relatedpeer) { /* Send first notification */
ASTOBJ_WRLOCK(p->relatedpeer);
sip_send_mwi_to_peer(p->relatedpeer);
ASTOBJ_UNLOCK(p->relatedpeer);
}
} else {
if ((firststate = ast_extension_state(NULL, p->context, p->exten)) < 0) {
Russell Bryant
committed
ast_log(LOG_ERROR, "Got SUBSCRIBE for extension %s@%s from %s, but there is no hint for that extension\n", p->exten, p->context, ast_inet_ntoa(p->sa.sin_addr));
Kevin P. Fleming
committed
transmit_response(p, "404 Not found", req);
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
return 0;
} else {
/* XXX reduce nesting here */
Kevin P. Fleming
committed
struct sip_pvt *p_old;
transmit_response(p, "200 OK", req);
transmit_state_notify(p, firststate, 1); /* Send first notification */
append_history(p, "Subscribestatus", "%s", ast_extension_state2str(firststate));
/* hide the 'complete' exten/context in the refer_to field for later display */
Olle Johansson
committed
ast_string_field_build(p, subscribeuri, "%s@%s", p->exten, p->context);
Kevin P. Fleming
committed
13839
13840
13841
13842
13843
13844
13845
13846
13847
13848
13849
13850
13851
13852
13853
13854
13855
13856
13857
13858
13859
13860
/* remove any old subscription from this peer for the same exten/context,
as the peer has obviously forgotten about it and it's wasteful to wait
for it to expire and send NOTIFY messages to the peer only to have them
ignored (or generate errors)
*/
ast_mutex_lock(&iflock);
for (p_old = iflist; p_old; p_old = p_old->next) {
if (p_old == p)
continue;
if (p_old->initreq.method != SIP_SUBSCRIBE)
continue;
if (p_old->subscribed == NONE)
continue;
ast_mutex_lock(&p_old->lock);
if (!strcmp(p_old->username, p->username)) {
if (!strcmp(p_old->exten, p->exten) &&
!strcmp(p_old->context, p->context)) {
ast_set_flag(&p_old->flags[0], SIP_NEEDDESTROY);
ast_mutex_unlock(&p_old->lock);
break;
}
Kevin P. Fleming
committed
ast_mutex_unlock(&p_old->lock);
Kevin P. Fleming
committed
ast_mutex_unlock(&iflock);
Kevin P. Fleming
committed
}
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
Kevin P. Fleming
committed
if (authpeer)
ASTOBJ_UNREF(authpeer, sip_destroy_peer);
return 1;
}
/*! \brief Handle incoming REGISTER request */
static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, char *e)
enum check_auth_result res;
/* Use this as the basis */
if (ast_test_flag(req, SIP_PKT_DEBUG))
ast_verbose("Using latest REGISTER request as basis request\n");
copy_request(&p->initreq, req);
check_via(p, req);
13885
13886
13887
13888
13889
13890
13891
13892
13893
13894
13895
13896
13897
13898
13899
13900
13901
13902
13903
13904
if ((res = register_verify(p, sin, req, e)) < 0) {
const char *reason = "";
switch (res) {
case AUTH_SECRET_FAILED:
reason = "Wrong password";
break;
case AUTH_USERNAME_MISMATCH:
reason = "Username/auth name mismatch";
break;
case AUTH_NOT_FOUND:
reason = "No matching peer found";
break;
case AUTH_UNKNOWN_DOMAIN:
reason = "Not a local domain";
break;
default:
break;
}
ast_log(LOG_NOTICE, "Registration from '%s' failed for '%s' - %s\n",
Russell Bryant
committed
get_header(req, "To"), ast_inet_ntoa(sin->sin_addr),
reason);
}
if (res < 1) {
/* Destroy the session, but keep us around for just a bit in case they don't
get our 200 OK */
sip_scheddestroy(p, 15000);
append_history(p, "RegRequest", "%s : Account %s", res ? "Failed": "Succeeded", get_header(req, "To"));
return res;
}
Kevin P. Fleming
committed
/*! \brief Handle incoming SIP requests (methods)
\note This is where all incoming requests go first */
/* called with p and p->owner locked */
Mark Spencer
committed
static int handle_request(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int *recount, int *nounlock)
/* Called with p->lock held, as well as p->owner->lock if appropriate, keeping things
relatively static */
const char *cmd;
const char *cseq;
const char *useragent;
int ignore = FALSE;
int error = 0;
/* Clear out potential response */
memset(&resp, 0, sizeof(resp));
/* Get Method and Cseq */
cseq = get_header(req, "Cseq");
cmd = req->header[0];
if (ast_strlen_zero(cmd) || ast_strlen_zero(cseq)) {
ast_log(LOG_ERROR, "Missing Cseq. Dropping this SIP message, it's incomplete.\n");
error = 1;
}
if (!error && sscanf(cseq, "%d%n", &seqno, &len) != 1) {
ast_log(LOG_ERROR, "No seqno in '%s'. Dropping incomplete message.\n", cmd);
error = 1;
}
if (error) {
if (!p->initreq.header) /* New call */
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); /* Make sure we destroy this dialog */
/* Get the command XXX */
cmd = req->rlPart1;
e = req->rlPart2;
/* Save useragent of the client */
useragent = get_header(req, "User-Agent");
if (!ast_strlen_zero(useragent))
ast_string_field_set(p, useragent, useragent);
/* Find out SIP method for incoming request */
if (req->method == SIP_RESPONSE) { /* Response to our request */
/* Response to our request -- Do some sanity checks */
if (!p->initreq.headers) {
if (option_debug)
ast_log(LOG_DEBUG, "That's odd... Got a response on a call we dont know about. Cseq %d Cmd %s\n", seqno, cmd);
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
return 0;
} else if (p->ocseq && (p->ocseq < seqno)) {
if (option_debug)
ast_log(LOG_DEBUG, "Ignoring out of order response %d (expecting %d)\n", seqno, p->ocseq);
return -1;
} else if (p->ocseq && (p->ocseq != seqno)) {
/* ignore means "don't do anything with it" but still have to
respond appropriately */
ignore = TRUE;
ast_set_flag(req, SIP_PKT_IGNORE);
ast_set_flag(req, SIP_PKT_IGNORE_RESP);
append_history(p, "Ignore", "Ignoring this retransmit\n");
e = ast_skip_blanks(e);
Kevin P. Fleming
committed
if (sscanf(e, "%d %n", &respid, &len) != 1) {
ast_log(LOG_WARNING, "Invalid response: '%s'\n", e);
Mark Spencer
committed
} else {
Mark Spencer
committed
/* More SIP ridiculousness, we have to ignore bogus contacts in 100 etc responses */
if ((respid == 200) || ((respid >= 300) && (respid <= 399)))
extract_uri(p, req);
handle_response(p, respid, e + len, req, ignore, seqno);
Mark Spencer
committed
}
return 0;
}