Newer
Older
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 */
ast_debug(3, "%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);
/* 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
was sent, so let's just give up retransmissions and waiting for
ACK on our error code. The call is hanging up any way. */
if (p->invitestate == INV_TERMINATED)
__sip_pretend_ack(p);
else
p->invitestate = INV_CANCELLED;
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);
ast_debug(1, "Got CANCEL on an answered call. Ignoring... \n");
if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD))
update_call_counter(p, DEC_CALL_LIMIT);
Olle Johansson
committed
stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
if (p->owner)
ast_queue_hangup(p->owner);
else
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
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;
}
}
static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen)
{
struct sip_pvt *p = chan->tech_pvt;
char *all = "", *parse = ast_strdupa(preparse);
Joshua Colp
committed
int res = 0;
AST_APP_ARG(type);
AST_APP_ARG(field);
);
AST_STANDARD_APP_ARGS(args, parse);
if (!IS_SIP_TECH(chan->tech)) {
ast_log(LOG_ERROR, "Cannot call %s on a non-SIP channel\n", funcname);
Joshua Colp
committed
memset(buf, 0, buflen);
Joshua Colp
committed
if (!strcasecmp(args.param, "rtpdest")) {
struct sockaddr_in sin;
Joshua Colp
committed
16085
16086
16087
16088
16089
16090
16091
16092
16093
16094
16095
16096
16097
16098
16099
16100
16101
16102
16103
16104
16105
16106
16107
16108
16109
16110
16111
16112
16113
16114
16115
16116
16117
16118
if (ast_strlen_zero(args.type))
args.type = "audio";
if (!strcasecmp(args.type, "audio"))
ast_rtp_get_peer(p->rtp, &sin);
else if (!strcasecmp(args.type, "video"))
ast_rtp_get_peer(p->vrtp, &sin);
else if (!strcasecmp(args.type, "text"))
ast_rtp_get_peer(p->trtp, &sin);
snprintf(buf, buflen, "%s:%d", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
} else if (!strcasecmp(args.param, "rtpqos")) {
struct ast_rtp_quality qos;
memset(&qos, 0, sizeof(qos));
if (ast_strlen_zero(args.type))
args.type = "audio";
if (ast_strlen_zero(args.field))
args.field = "all";
if (strcasecmp(args.type, "AUDIO") == 0) {
all = ast_rtp_get_quality(p->rtp, &qos);
} else if (strcasecmp(args.type, "VIDEO") == 0) {
all = ast_rtp_get_quality(p->vrtp, &qos);
} else if (strcasecmp(args.type, "TEXT") == 0) {
all = ast_rtp_get_quality(p->trtp, &qos);
}
if (strcasecmp(args.field, "local_ssrc") == 0)
snprintf(buf, buflen, "%u", qos.local_ssrc);
else if (strcasecmp(args.field, "local_lostpackets") == 0)
snprintf(buf, buflen, "%u", qos.local_lostpackets);
else if (strcasecmp(args.field, "local_jitter") == 0)
snprintf(buf, buflen, "%.0f", qos.local_jitter * 1000.0);
Joshua Colp
committed
else if (strcasecmp(args.field, "local_count") == 0)
snprintf(buf, buflen, "%u", qos.local_count);
else if (strcasecmp(args.field, "remote_ssrc") == 0)
snprintf(buf, buflen, "%u", qos.remote_ssrc);
else if (strcasecmp(args.field, "remote_lostpackets") == 0)
snprintf(buf, buflen, "%u", qos.remote_lostpackets);
else if (strcasecmp(args.field, "remote_jitter") == 0)
snprintf(buf, buflen, "%.0f", qos.remote_jitter * 1000.0);
Joshua Colp
committed
else if (strcasecmp(args.field, "remote_count") == 0)
snprintf(buf, buflen, "%u", qos.remote_count);
else if (strcasecmp(args.field, "rtt") == 0)
snprintf(buf, buflen, "%.0f", qos.rtt * 1000.0);
Joshua Colp
committed
else if (strcasecmp(args.field, "all") == 0)
ast_copy_string(buf, all, buflen);
else {
ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname);
return -1;
}
} else {
res = -1;
Joshua Colp
committed
return res;
/*! \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;
/* If we have an INCOMING invite that we haven't answered, terminate that transaction */
if (p->pendinginvite && !ast_test_flag(&p->flags[0], SIP_OUTGOING) && !req->ignore && !p->owner)
transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
Olle Johansson
committed
p->invitestate = INV_TERMINATED;
copy_request(&p->initreq, req);
if (sipdebug)
ast_debug(1, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
check_via(p, req);
/* Get RTCP quality before end of call */
if (p->do_history || p->owner) {
char *audioqos, *videoqos, *textqos;
audioqos = ast_rtp_get_quality(p->rtp, NULL);
append_history(p, "RTCPaudio", "Quality:%s", audioqos);
if (p->owner)
pbx_builtin_setvar_helper(p->owner, "RTPAUDIOQOS", audioqos);
}
if (p->vrtp) {
videoqos = ast_rtp_get_quality(p->vrtp, NULL);
append_history(p, "RTCPvideo", "Quality:%s", videoqos);
if (p->owner)
pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", videoqos);
}
textqos = ast_rtp_get_quality(p->trtp, NULL);
append_history(p, "RTCPtext", "Quality:%s", textqos);
if (p->owner)
pbx_builtin_setvar_helper(p->owner, "RTPTEXTQOS", textqos);
}
Olle Johansson
committed
stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
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);
ast_debug(3, "Received bye, issuing owner hangup\n");
} else {
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
ast_debug(3, "Received bye, no owner, selfdestruct soon.\n");
}
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 (!req->ignore) {
if (req->debug)
Kevin P. Fleming
committed
ast_verbose("Receiving message!\n");
receive_message(p, req);
} else
transmit_response(p, "202 Accepted", req);
return 1;
}
Russell Bryant
committed
static void add_peer_mwi_subs(struct sip_peer *peer)
{
struct sip_mailbox *mailbox;
AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
mailbox->event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, peer,
AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox->mailbox,
AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, S_OR(mailbox->context, "default"),
AST_EVENT_IE_END);
}
}
/*! \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;
Olle Johansson
committed
const char *eventheader = get_header(req, "Event"); /* Get Event package name */
const char *accept = get_header(req, "Accept");
int resubscribe = (p->subscribed != NONE);
Olle Johansson
committed
char *temp, *event;
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_debug(1, "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);
Kevin P. Fleming
committed
return 0;
} else if (req->debug) {
if (resubscribe)
ast_debug(1, "Got a re-subscribe on existing subscription %s\n", p->callid);
else
ast_debug(1, "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);
p->needdestroy = 1;
if (!req->ignore && !resubscribe) { /* Set up dialog, new subscription */
/* Use this as the basis */
if (req->debug)
Kevin P. Fleming
committed
ast_verbose("Creating new subscription\n");
copy_request(&p->initreq, req);
if (sipdebug)
ast_debug(4, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
check_via(p, req);
} else if (req->debug && req->ignore)
ast_verbose("Ignoring this SUBSCRIBE request\n");
/* Find parameters to Event: header value and remove them for now */
Olle Johansson
committed
if (ast_strlen_zero(eventheader)) {
transmit_response(p, "489 Bad Event", req);
ast_debug(2, "Received SIP subscribe for unknown event package: <none>\n");
p->needdestroy = 1;
Olle Johansson
committed
return 0;
}
if ( (strchr(eventheader, ';'))) {
event = ast_strdupa(eventheader); /* Since eventheader is a const, we can't change it */
temp = strchr(event, ';');
*temp = '\0'; /* Remove any options for now */
/* We might need to use them later :-) */
} else
event = (char *) eventheader; /* XXX is this legal ? */
Olle Johansson
committed
/* Handle authentication */
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) /* authpeer = NULL here */
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);
p->needdestroy = 1;
return 0;
}
/* At this point, authpeer cannot be NULL. Remember we hold a reference,
* so we must release it when done.
* XXX must remove all the checks for authpeer == NULL.
*/
/* 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);
p->needdestroy = 1;
unref_peer(authpeer);
return 0;
}
/* Get destination right away */
gotdest = get_destination(p, NULL);
Olle Johansson
committed
/* Get full contact header - this needs to be used as a request URI in NOTIFY's */
parse_ok_contact(p, req);
build_contact(p);
if (gotdest) {
transmit_response(p, "404 Not Found", req);
p->needdestroy = 1;
unref_peer(authpeer);
/* 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 */
if (authpeer) /* We do not need the authpeer any more */
unref_peer(authpeer);
/* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */
/* Polycom phones only handle xpidf+xml, even if they say they can
handle pidf+xml as well
*/
if (strstr(p->useragent, "Polycom")) {
p->subscribed = XPIDF_XML;
} else 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 (ast_strlen_zero(accept)) {
if (p->subscribed == NONE) { /* if the subscribed field is not already set, and there is no accept header... */
transmit_response(p, "489 Bad Event", req);
ast_log(LOG_WARNING,"SUBSCRIBE failure: no Accept header: pvt: stateid: %d, laststate: %d, dialogver: %d, subscribecont: '%s', subscribeuri: '%s'\n",
p->stateid, p->laststate, p->dialogver, p->subscribecontext, p->subscribeuri);
p->needdestroy = 1;
return 0;
}
/* if p->subscribed is non-zero, then accept is not obligatory; according to rfc 3265 section 3.1.3, at least.
so, we'll just let it ride, keeping the value from a previous subscription, and not abort the subscription */
} else {
/* Can't find a format for events that we know about */
char mybuf[200];
snprintf(mybuf,sizeof(mybuf),"489 Bad Event (format %s)", accept);
transmit_response(p, mybuf, req);
ast_log(LOG_WARNING,"SUBSCRIBE failure: unrecognized format: '%s' pvt: subscribed: %d, stateid: %d, laststate: %d, dialogver: %d, subscribecont: '%s', subscribeuri: '%s'\n",
accept, (int)p->subscribed, p->stateid, p->laststate, p->dialogver, p->subscribecontext, p->subscribeuri);
p->needdestroy = 1;
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);
ast_debug(2, "Received SIP mailbox subscription for unknown format: %s\n", accept);
p->needdestroy = 1;
unref_peer(authpeer);
/* 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
*/
Russell Bryant
committed
if (!authpeer || AST_LIST_EMPTY(&authpeer->mailboxes)) {
transmit_response(p, "404 Not found (no mailbox)", req);
p->needdestroy = 1;
ast_log(LOG_NOTICE, "Received SIP subscribe for peer without mailbox: %s\n", authpeer->name);
unref_peer(authpeer);
return 0;
}
p->subscribed = MWI_NOTIFICATION;
if (ast_test_flag(&authpeer->flags[1], SIP_PAGE2_SUBSCRIBEMWIONLY)) {
Russell Bryant
committed
add_peer_mwi_subs(authpeer);
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 */
/* Do not release authpeer here */
} else { /* At this point, Asterisk does not understand the specified event */
transmit_response(p, "489 Bad Event", req);
ast_debug(2, "Received SIP subscribe for unknown event package: %s\n", event);
p->needdestroy = 1;
unref_peer(authpeer);
}
/* Add subscription for extension state from the PBX core */
if (p->subscribed != MWI_NOTIFICATION && !resubscribe) {
if (p->stateid > -1)
ast_extension_state_del(p->stateid, cb_extensionstate);
p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, p);
if (!req->ignore && p)
p->lastinvite = seqno;
if (p && !p->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;
if (sipdebug) {
Kevin P. Fleming
committed
if (p->subscribed == MWI_NOTIFICATION && p->relatedpeer)
Russell Bryant
committed
ast_debug(2, "Adding subscription for mailbox notification - peer %s\n", p->relatedpeer->name);
Kevin P. Fleming
committed
else
ast_debug(2, "Adding subscription for extension %s context %s for peer %s\n", p->exten, p->context, p->username);
Kevin P. Fleming
committed
}
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, NULL, 0);
Kevin P. Fleming
committed
ASTOBJ_UNLOCK(p->relatedpeer);
}
} else {
Kevin P. Fleming
committed
if ((firststate = ast_extension_state(NULL, p->context, p->exten)) < 0) {
ast_log(LOG_NOTICE, "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);
p->needdestroy = 1;
Kevin P. Fleming
committed
return 0;
}
transmit_response(p, "200 OK", req);
Olle Johansson
committed
transmit_state_notify(p, firststate, 1, FALSE); /* 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 */
ast_string_field_build(p, subscribeuri, "%s@%s", p->exten, p->context);
/* 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)
*/
Olle Johansson
committed
for (p_old = dialoglist; 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;
sip_pvt_lock(p_old);
if (!strcmp(p_old->username, p->username)) {
if (!strcmp(p_old->exten, p->exten) &&
!strcmp(p_old->context, p->context)) {
p_old->needdestroy = 1;
sip_pvt_unlock(p_old);
sip_pvt_unlock(p_old);
Kevin P. Fleming
committed
}
p->needdestroy = 1;
}
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 */
copy_request(&p->initreq, req);
if (sipdebug)
ast_debug(4, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
check_via(p, req);
if ((res = register_verify(p, sin, req, e)) < 0) {
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;
case AUTH_PEER_NOT_DYNAMIC:
reason = "Peer is not supposed to register";
break;
case AUTH_ACL_FAILED:
reason = "Device does not match ACL";
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);
append_history(p, "RegRequest", "Failed : Account %s : %s", get_header(req, "To"), reason);
} else
append_history(p, "RegRequest", "Succeeded : Account %s", get_header(req, "To"));
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, DEFAULT_TRANS_TIMEOUT);
}
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 */
static int handle_incoming(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 error = 0;
/* 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.headers) /* New call */
p->needdestroy = 1; /* 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 */
/* When we get here, we know this is a SIP dialog where we've sent
* a request and have a response, or at least get a response
* within an existing dialog. Do some sanity checks, then
* possibly process the request. In all cases, there function
* terminates at the end of this block
*/
int ret = 0;
if (p->ocseq < seqno && seqno != p->lastnoninvite) {
ast_debug(1, "Ignoring out of order response %d (expecting %d)\n", seqno, p->ocseq);
ret = -1;
} else if (p->ocseq != seqno && seqno != p->lastnoninvite) {
/* ignore means "don't do anything with it" but still have to
* respond appropriately.
* But in this case this is a response already, so we really
* have nothing to do with this message, and even setting the
* ignore flag is pointless.
*/
req->ignore = 1;
append_history(p, "Ignore", "Ignoring this retransmit\n");
} else if (e) {
e = ast_skip_blanks(e);
if (sscanf(e, "%d %n", &respid, &len) != 1) {
ast_log(LOG_WARNING, "Invalid response: '%s'\n", e);
/* XXX maybe should do ret = -1; */
} else if (respid <= 0) {
ast_log(LOG_WARNING, "Invalid SIP response code: '%d'\n", respid);
/* XXX maybe should do ret = -1; */
} else { /* finally, something worth processing */
/* 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, seqno);
Mark Spencer
committed
}
return 0;
}
/* New SIP request coming in
(could be new request in existing SIP dialog as well...)
*/
p->method = req->method; /* Find out which SIP method they are using */
ast_debug(4, "**** Received %s (%d) - Command in SIP %s\n", sip_methods[p->method].text, sip_methods[p->method].id, cmd);
if (p->icseq && (p->icseq > seqno)) {
ast_debug(1, "Ignoring too old SIP packet packet %d (expecting >= %d)\n", seqno, p->icseq);
if (req->method != SIP_ACK)
transmit_response(p, "503 Server error", req); /* We must respond according to RFC 3261 sec 12.2 */
} else if (p->icseq &&
p->icseq == seqno &&
req->method != SIP_ACK &&
(p->method != SIP_CANCEL || p->alreadygone)) {
/* ignore means "don't do anything with it" but still have to
respond appropriately. We do this if we receive a repeat of
the last sequence number */
req->ignore = 1;
ast_debug(3, "Ignoring SIP message because of retransmit (%s Seqno %d, ours %d)\n", sip_methods[p->method].text, p->icseq, seqno);
if (seqno >= p->icseq)
/* Next should follow monotonically (but not necessarily
incrementally -- thanks again to the genius authors of SIP --
increasing */
p->icseq = seqno;
/* Find their tag if we haven't got it */
if (ast_strlen_zero(p->theirtag)) {
char tag[128];
gettag(req, "From", tag, sizeof(tag));
ast_string_field_set(p, theirtag, tag);
}
snprintf(p->lastmsg, sizeof(p->lastmsg), "Rx: %s", cmd);
if (pedanticsipchecking) {
/* If this is a request packet without a from tag, it's not
correct according to RFC 3261 */
/* Check if this a new request in a new dialog with a totag already attached to it,
RFC 3261 - section 12.2 - and we don't want to mess with recovery */
if (!p->initreq.headers && req->has_to_tag) {
/* If this is a first request and it got a to-tag, it is not for us */
if (!req->ignore && req->method == SIP_INVITE) {
transmit_response_reliable(p, "481 Call/Transaction Does Not Exist", req);
/* Will cease to exist after ACK */
transmit_response(p, "481 Call/Transaction Does Not Exist", req);
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
ast_debug(1, "Got ACK for unknown dialog... strange.\n");
}
return res;
}
}
if (!e && (p->method == SIP_INVITE || p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER || p->method == SIP_NOTIFY)) {
transmit_response(p, "400 Bad request", req);
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
return -1;
}
/* Handle various incoming SIP methods in requests */
switch (p->method) {
case SIP_OPTIONS:
res = handle_request_options(p, req);
break;
case SIP_INVITE:
res = handle_request_invite(p, req, debug, seqno, sin, recount, e, nounlock);
break;
case SIP_REFER:
res = handle_request_refer(p, req, debug, seqno, nounlock);
break;
case SIP_CANCEL:
res = handle_request_cancel(p, req);
break;
case SIP_BYE:
res = handle_request_bye(p, req);
break;
case SIP_MESSAGE:
res = handle_request_message(p, req);
break;
case SIP_SUBSCRIBE:
res = handle_request_subscribe(p, req, sin, seqno, e);
break;
case SIP_REGISTER:
res = handle_request_register(p, req, sin, e);
break;
case SIP_INFO:
if (req->debug)
ast_verbose("Receiving INFO!\n");
if (!req->ignore)
handle_request_info(p, req);
else /* if ignoring, transmit response */
transmit_response(p, "200 OK", req);
break;
case SIP_NOTIFY:
res = handle_request_notify(p, req, sin, seqno, e);
break;
case SIP_ACK:
/* Make sure we don't ignore this */
if (seqno == p->pendinginvite) {
p->invitestate = INV_TERMINATED;
p->pendinginvite = 0;
__sip_ack(p, seqno, 1 /* response */, 0);
if (find_sdp(req)) {
if (process_sdp(p, req))
return -1;
}
check_pendings(p);
}
Olle Johansson
committed
/* Got an ACK that we did not match. Ignore silently */
if (!p->lastinvite && ast_strlen_zero(p->randdata))
p->needdestroy = 1;
break;
default:
Kevin P. Fleming
committed
transmit_response_with_allow(p, "501 Method Not Implemented", req, 0);
ast_log(LOG_NOTICE, "Unknown SIP command '%s' from '%s'\n",
Russell Bryant
committed
cmd, ast_inet_ntoa(p->sa.sin_addr));
/* If this is some new method, and we don't have a call, destroy it now */
if (!p->initreq.headers)
p->needdestroy = 1;
\note sipsock_read locks the owner channel while we are processing the SIP message
\note Successful messages is connected to SIP call and forwarded to handle_incoming()
static int sipsock_read(int *id, int fd, short events, void *ignore)
{
struct sip_request req;
socklen_t len = sizeof(sin);
Mark Spencer
committed
int nounlock;
res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len);
if (res < 0) {
if (errno == EAGAIN)
ast_log(LOG_NOTICE, "SIP: Received packet with bad UDP checksum\n");
else
#endif
if (errno != ECONNREFUSED)
ast_log(LOG_WARNING, "Recv error: %s\n", strerror(errno));
return 1;
}
if (res == sizeof(req.data)) {
ast_debug(1, "Received packet exceeds buffer. Data is possibly lost\n");
req.data[sizeof(req.data) - 1] = '\0';
} else
req.data[res] = '\0';
Olle Johansson
committed
if(sip_debug_test_addr(&sin)) /* Set the debug flag early on packet level */
req.debug = 1;
if (pedanticsipchecking)
req.len = lws2sws(req.data, req.len); /* Fix multiline headers */
if (req.debug)
ast_verbose("\n<--- SIP read from %s:%d --->\n%s\n<------------->\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), req.data);
Olle Johansson
committed
parse_request(&req);
req.method = find_sip_method(req.rlPart1);
if (req.debug)
ast_verbose("--- (%d headers %d lines)%s ---\n", req.headers, req.lines, (req.headers + req.lines == 0) ? " Nat keepalive" : "");
if (req.headers < 2) /* Must have at least two headers */
Kevin P. Fleming
committed
/* Process request, with netlock held, and with usual deadlock avoidance */
for (lockretry = 100; lockretry > 0; lockretry--) {
ast_mutex_lock(&netlock);
Kevin P. Fleming
committed
/* Find the active SIP dialog or create a new one */
p = find_call(&req, &sin, req.method); /* returns p locked */
if (p == NULL) {
ast_debug(1, "Invalid SIP message - rejected , no callid, len %d\n", req.len);
return 1;
}
/* Go ahead and lock the owner if it has one -- we may need it */
/* becaues this is deadlock-prone, we need to try and unlock if failed */
if (!p->owner || !ast_channel_trylock(p->owner))
break; /* locking succeeded */
ast_debug(1, "Failed to grab owner channel lock, trying again. (SIP call %s)\n", p->callid);
sip_pvt_unlock(p);
ast_mutex_unlock(&netlock);
/* Sleep for a very short amount of time */
usleep(1);
}
p->recv = sin;
if (p->do_history) /* This is a request or response, note what it was for */
append_history(p, "Rx", "%s / %s / %s", req.data, get_header(&req, "CSeq"), req.rlPart2);
if (!lockretry) {
if (p->owner)
ast_log(LOG_ERROR, "We could NOT get the channel lock for %s! \n", S_OR(p->owner->name, "- no channel name ??? - "));
ast_log(LOG_ERROR, "SIP transaction failed: %s \n", p->callid);
if (req.method != SIP_ACK)
transmit_response(p, "503 Server error", &req); /* We must respond according to RFC 3261 sec 12.2 */
/* XXX We could add retry-after to make sure they come back */
append_history(p, "LockFail", "Owner lock failed, transaction failed.");
return 1;
if (handle_incoming(p, &req, &sin, &recount, &nounlock) == -1) {
/* Request failed */
ast_debug(1, "SIP message could not be handled, bad request: %-70.70s\n", p->callid[0] ? p->callid : "<no callid>");
}
if (p->owner && !nounlock)
ast_channel_unlock(p->owner);
sip_pvt_unlock(p);
ast_mutex_unlock(&netlock);
if (recount)
ast_update_use_count();
Russell Bryant
committed
/*!
* \brief Get cached MWI info
* \retval 0 At least one message is waiting
* \retval 1 no messages waiting
*/
static int get_cached_mwi(struct sip_peer *peer, int *new, int *old)
{
struct sip_mailbox *mailbox;
AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
struct ast_event *event;
event = ast_event_get_cached(AST_EVENT_MWI,
AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox->mailbox,
Russell Bryant
committed
AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, S_OR(mailbox->context, "default"),
Russell Bryant
committed
AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
AST_EVENT_IE_END);
if (!event)
continue;
*new += ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
*old += ast_event_get_ie_uint(event, AST_EVENT_IE_OLDMSGS);
ast_event_destroy(event);
}
return (*new || *old) ? 0 : 1;
}
/*! \brief Send message waiting indication to alert peer that they've got voicemail */
static int sip_send_mwi_to_peer(struct sip_peer *peer, const struct ast_event *event, int cache_only)
{
/* Called with peerl lock, but releases it */
struct sip_pvt *p;
if (ast_test_flag((&peer->flags[1]), SIP_PAGE2_SUBSCRIBEMWIONLY) && !peer->mwipvt)
Kevin P. Fleming
committed
/* Do we have an IP address? If not, skip this peer */
if (!peer->addr.sin_addr.s_addr && !peer->defaddr.sin_addr.s_addr)
return 0;
if (event) {
newmsgs = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
oldmsgs = ast_event_get_ie_uint(event, AST_EVENT_IE_OLDMSGS);
Russell Bryant
committed
} else if (!get_cached_mwi(peer, &newmsgs, &oldmsgs)) {
/* got it! Don't keep looking. */
} else if (cache_only) {
return 0;
Russell Bryant
committed
} else { /* Fall back to manually checking the mailbox */
struct ast_str *mailbox_str = ast_str_alloca(512);
peer_mailboxes_to_str(&mailbox_str, peer);
ast_app_inboxcount(mailbox_str->str, &newmsgs, &oldmsgs);
}
Kevin P. Fleming
committed
if (peer->mwipvt) {
/* Base message on subscription */
p = dialog_ref(peer->mwipvt);
Kevin P. Fleming
committed
} else {
/* Build temporary dialog for this message */
Olle Johansson
committed
if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY)))
Kevin P. Fleming
committed
return -1;
if (create_addr_from_peer(p, peer)) {
/* Maybe they're not registered, etc. */
sip_destroy(p);
return 0;
}
/* Recalculate our side, and recalculate Call ID */
ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
Kevin P. Fleming
committed
build_via(p);
build_callid_pvt(p);
/* Destroy this session after 32 secs */
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
ast_set_flag(&p->flags[0], SIP_OUTGOING);
Kevin P. Fleming
committed
transmit_notify_with_mwi(p, newmsgs, oldmsgs, peer->vmexten);
Kevin P. Fleming
committed
Kevin P. Fleming
committed
}
/*! \brief helper function for the monitoring thread */
static void check_rtp_timeout(struct sip_pvt *dialog, time_t t)
{
/* If we have no RTP or no active owner, no need to check timers */
if (!dialog->rtp || !dialog->owner)
return;
/* If the call is not in UP state or redirected outside Asterisk, no need to check timers */
if (dialog->owner->_state != AST_STATE_UP || dialog->redirip.sin_addr.s_addr)
return;
/* If the call is involved in a T38 fax session do not check RTP timeout */
if (dialog->t38.state == T38_ENABLED)
return;
/* If we have no timers set, return now */
Joshua Colp
committed
if ((ast_rtp_get_rtpkeepalive(dialog->rtp) == 0) && (ast_rtp_get_rtptimeout(dialog->rtp) == 0) && (ast_rtp_get_rtpholdtimeout(dialog->rtp) == 0))