Newer
Older
static struct sip_peer *temp_peer(const char *name);
Kevin P. Fleming
committed
static int sip_send_mwi_to_peer(struct sip_peer *peer);
static int sip_scheddestroy(struct sip_pvt *p, int ms);
/*----- RTP interface functions */
static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active);
static struct ast_rtp *sip_get_rtp_peer(struct ast_channel *chan);
static struct ast_rtp *sip_get_vrtp_peer(struct ast_channel *chan);
static int sip_get_codec(struct ast_channel *chan);
/*! \brief Definition of this channel for PBX channel registration */
static const struct ast_channel_tech sip_tech = {
.description = "Session Initiation Protocol (SIP)",
.capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1),
Mark Spencer
committed
.properties = AST_CHAN_TP_WANTSJITTER,
.requester = sip_request_call,
.devicestate = sip_devicestate,
.call = sip_call,
.hangup = sip_hangup,
.answer = sip_answer,
.read = sip_read,
.write = sip_write,
.write_video = sip_write,
.indicate = sip_indicate,
.transfer = sip_transfer,
.fixup = sip_fixup,
.send_digit = sip_senddigit,
.bridge = ast_rtp_bridge,
.send_text = sip_sendtext,
};
/*! \brief Interface structure with callbacks used to connect to RTP module */
static struct ast_rtp_protocol sip_rtp = {
get_rtp_info: sip_get_rtp_peer,
get_vrtp_info: sip_get_vrtp_peer,
set_rtp_peer: sip_set_rtp_peer,
get_codec: sip_get_codec,
};
/*! \brief returns true if 'name' (with optional trailing whitespace)
* matches the sip method 'id'.
* Strictly speaking, SIP methods are case SENSITIVE, but we do
* a case-insensitive comparison to be more tolerant.
* following Jon Postel's rule: Be gentle in what you accept, strict with what you send
*/
static int method_match(enum sipmethod id, const char *name)
{
int len = strlen(sip_methods[id].text);
int l_name = name ? strlen(name) : 0;
/* true if the string is long enough, and ends with whitespace, and matches */
return (l_name >= len && name[len] < 33 &&
!strncasecmp(sip_methods[id].text, name, len));
}
/*! \brief find_sip_method: Find SIP method from header */
static int find_sip_method(const char *msg)
{
int i, res = 0;
if (ast_strlen_zero(msg))
return 0;
for (i = 1; i < (sizeof(sip_methods) / sizeof(sip_methods[0])) && !res; i++) {
res = sip_methods[i].id;
}
return res;
}
/*! \brief Parse supported header in incoming packet */
static unsigned int parse_sip_options(struct sip_pvt *pvt, const char *supported)
{
char *temp = ast_strdupa(supported);
unsigned int profile = 0;
if (!pvt || ast_strlen_zero(supported) )
return 0;
Russell Bryant
committed
if (option_debug > 2 && sipdebug)
ast_log(LOG_DEBUG, "Begin: parsing SIP \"Supported: %s\"\n", supported);
if ( (sep = strchr(next, ',')) != NULL)
*sep++ = '\0';
next = ast_skip_blanks(next);
Russell Bryant
committed
if (option_debug > 2 && sipdebug)
ast_log(LOG_DEBUG, "Found SIP option: -%s-\n", next);
for (i=0; i < (sizeof(sip_options) / sizeof(sip_options[0])); i++) {
if (!strcasecmp(next, sip_options[i].text)) {
profile |= sip_options[i].id;
Russell Bryant
committed
if (option_debug > 2 && sipdebug)
ast_log(LOG_DEBUG, "Matched SIP option: %s\n", next);
}
}
if (!found && option_debug > 2 && sipdebug)
ast_log(LOG_DEBUG, "Found no match for SIP option: %s (Please file bug report!)\n", next);
}
}
/*! \brief See if we pass debug IP filter */
static inline int sip_debug_test_addr(const struct sockaddr_in *addr)
Russell Bryant
committed
if (!sipdebug)
return 0;
if (debugaddr.sin_addr.s_addr) {
if (((ntohs(debugaddr.sin_port) != 0)
&& (debugaddr.sin_port != addr->sin_port))
|| (debugaddr.sin_addr.s_addr != addr->sin_addr.s_addr))
return 0;
}
return 1;
}
/* The real destination address for a write */
static const struct sockaddr_in *sip_real_dst(const struct sip_pvt *p)
{
return ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE ? &p->recv : &p->sa;
}
static const char *sip_nat_mode(const struct sip_pvt *p)
{
return ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE ? "NAT" : "no NAT";
}
/*! \brief Test PVT for debugging output */
static inline int sip_debug_test_pvt(struct sip_pvt *p)
{
Russell Bryant
committed
if (!sipdebug)
return sip_debug_test_addr(sip_real_dst(p));
/*! \brief Transmit SIP message */
static int __sip_xmit(struct sip_pvt *p, char *data, int len)
{
int res;
char iabuf[INET_ADDRSTRLEN];
const struct sockaddr_in *dst = sip_real_dst(p);
res=sendto(sipsock, data, len, 0, (const struct sockaddr *)dst, sizeof(struct sockaddr_in));
if (res != len)
ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s:%d returned %d: %s\n", data, len, ast_inet_ntoa(iabuf, sizeof(iabuf), dst->sin_addr), ntohs(dst->sin_port), res, strerror(errno));
/*! \brief Build a Via header for a request */
static void build_via(struct sip_pvt *p)
/* Work around buggy UNIDEN UIP200 firmware */
const char *rport = ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_RFC3581 ? ";rport" : "";
/* z9hG4bK is a magic cookie. See RFC 3261 section 8.1.1.7 */
ast_string_field_build(p, via, "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x%s",
ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch, rport);
/*! \brief NAT fix - decide which IP address to use for ASterisk server?
* Only used for outbound registrations */
static int ast_sip_ouraddrfor(struct in_addr *them, struct in_addr *us)
{
Mark Spencer
committed
* Using the localaddr structure built up with localnet statements
* apply it to their address to see if we need to substitute our
* externip or can get away with our internal bindaddr
*/
struct sockaddr_in theirs;
theirs.sin_addr = *them;
Mark Spencer
committed
if (localaddr && externip.sin_addr.s_addr &&
ast_apply_ha(localaddr, &theirs)) {
if (externexpire && time(NULL) >= externexpire) {
struct ast_hostent ahp;
struct hostent *hp;
time(&externexpire);
externexpire += externrefresh;
if ((hp = ast_gethostbyname(externhost, &ahp))) {
memcpy(&externip.sin_addr, hp->h_addr, sizeof(externip.sin_addr));
} else
ast_log(LOG_NOTICE, "Warning: Re-lookup of '%s' failed!\n", externhost);
}
*us = externip.sin_addr;
if (option_debug) {
char iabuf[INET_ADDRSTRLEN];
ast_inet_ntoa(iabuf, sizeof(iabuf), *(struct in_addr *)&them->s_addr);
ast_log(LOG_DEBUG, "Target address %s is not local, substituting externip\n", iabuf);
}
} else if (bindaddr.sin_addr.s_addr)
*us = bindaddr.sin_addr;
else
return ast_ouraddrfor(them, us);
return 0;
}
/*! \brief Append to SIP dialog history
Olle Johansson
committed
\return Always returns 0 */
#define append_history(p, event, fmt , args... ) append_history_full(p, "%-15s " fmt, event, ## args)
static int append_history_full(struct sip_pvt *p, const char *fmt, ...)
__attribute__ ((format (printf, 2, 3)));
/*! \brief Append to SIP dialog history with arg list */
Olle Johansson
committed
static void append_history_va(struct sip_pvt *p, const char *fmt, va_list ap)
Mark Spencer
committed
{
Olle Johansson
committed
char buf[80], *c = buf; /* max history length */
struct sip_history *hist;
int l;
Olle Johansson
committed
vsnprintf(buf, sizeof(buf), fmt, ap);
strsep(&c, "\r\n"); /* Trim up everything after \r or \n */
l = strlen(buf) + 1;
if (!(hist = ast_calloc(1, sizeof(*hist) + l)))
Olle Johansson
committed
return;
if (!p->history && !(p->history = ast_calloc(1, sizeof(*p->history)))) {
Olle Johansson
committed
free(hist);
return;
Mark Spencer
committed
}
Olle Johansson
committed
memcpy(hist->event, buf, l);
AST_LIST_INSERT_TAIL(p->history, hist, list);
}
/*! \brief Append to SIP dialog history with arg list */
Olle Johansson
committed
static int append_history_full(struct sip_pvt *p, const char *fmt, ...)
{
Olle Johansson
committed
if (!recordhistory || !p)
return 0;
va_start(ap, fmt);
append_history_va(p, fmt, ap);
va_end(ap);
Olle Johansson
committed
Mark Spencer
committed
}
/*! \brief Retransmit SIP message if no answer */
struct sip_pkt *pkt=data, *prev, *cur = NULL;
char iabuf[INET_ADDRSTRLEN];
int reschedule = DEFAULT_RETRANS;
/* Lock channel */
ast_mutex_lock(&pkt->owner->lock);
if (pkt->retrans < MAX_RETRANS) {
pkt->retrans++;
if (!pkt->timer_t1) { /* Re-schedule using timer_a and timer_t1 */
Russell Bryant
committed
if (sipdebug && option_debug > 3)
ast_log(LOG_DEBUG, "SIP TIMER: Not rescheduling id #%d:%s (Method %d) (No timer T1)\n", pkt->retransid, sip_methods[pkt->method].text, pkt->method);
} else {
int siptimer_a;
Russell Bryant
committed
if (sipdebug && option_debug > 3)
ast_log(LOG_DEBUG, "SIP TIMER: Rescheduling retransmission #%d (%d) %s - %d\n", pkt->retransid, pkt->retrans, sip_methods[pkt->method].text, pkt->method);
if (!pkt->timer_a)
pkt->timer_a = 2 ;
else
pkt->timer_a = 2 * pkt->timer_a;
/* For non-invites, a maximum of 4 secs */
siptimer_a = pkt->timer_t1 * pkt->timer_a; /* Double each time */
if (pkt->method != SIP_INVITE && siptimer_a > 4000)
siptimer_a = 4000;
/* Reschedule re-transmit */
reschedule = siptimer_a;
if (option_debug > 3)
ast_log(LOG_DEBUG, "** SIP timers: Rescheduling retransmission %d to %d ms (t1 %d ms (Retrans id #%d)) \n", pkt->retrans +1, siptimer_a, pkt->timer_t1, pkt->retransid);
}
if (pkt->owner && sip_debug_test_pvt(pkt->owner)) {
if (ast_test_flag(&pkt->owner->flags[0], SIP_NAT_ROUTE))
ast_verbose("Retransmitting #%d (NAT) to %s:%d:\n%s\n---\n", pkt->retrans, ast_inet_ntoa(iabuf, sizeof(iabuf), pkt->owner->recv.sin_addr), ntohs(pkt->owner->recv.sin_port), pkt->data);
ast_verbose("Retransmitting #%d (no NAT) to %s:%d:\n%s\n---\n", pkt->retrans, ast_inet_ntoa(iabuf, sizeof(iabuf), pkt->owner->sa.sin_addr), ntohs(pkt->owner->sa.sin_port), pkt->data);
Olle Johansson
committed
append_history(pkt->owner, "ReTx", "%d %s", reschedule, pkt->data);
__sip_xmit(pkt->owner, pkt->data, pkt->packetlen);
ast_mutex_unlock(&pkt->owner->lock);
return reschedule;
}
/* Too many retries */
if (pkt->owner && pkt->method != SIP_OPTIONS) {
if (ast_test_flag(pkt, FLAG_FATAL) || sipdebug) /* Tell us if it's critical or if we're debugging */
ast_log(LOG_WARNING, "Maximum retries exceeded on transmission %s for seqno %d (%s %s)\n", pkt->owner->callid, pkt->seqno, (ast_test_flag(pkt, FLAG_FATAL)) ? "Critical" : "Non-critical", (ast_test_flag(pkt, FLAG_RESPONSE)) ? "Response" : "Request");
} else {
Russell Bryant
committed
if ((pkt->method == SIP_OPTIONS) && sipdebug)
ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s) \n", pkt->owner->callid);
}
Olle Johansson
committed
append_history(pkt->owner, "MaxRetries", "%s", (ast_test_flag(pkt, FLAG_FATAL)) ? "(Critical)" : "(Non-critical)");
pkt->retransid = -1;
if (ast_test_flag(pkt, FLAG_FATAL)) {
while(pkt->owner->owner && ast_mutex_trylock(&pkt->owner->owner->lock)) {
ast_mutex_unlock(&pkt->owner->lock);
usleep(1);
ast_mutex_lock(&pkt->owner->lock);
if (pkt->owner->owner) {
ast_set_flag(&pkt->owner->flags[0], SIP_ALREADYGONE);
ast_log(LOG_WARNING, "Hanging up call %s - no reply to our critical packet.\n", pkt->owner->callid);
ast_queue_hangup(pkt->owner->owner);
ast_mutex_unlock(&pkt->owner->owner->lock);
} else {
/* If no channel owner, destroy now */
ast_set_flag(&pkt->owner->flags[0], SIP_NEEDDESTROY);
/* In any case, go ahead and remove the packet */
for (prev = NULL, cur = pkt->owner->packets; cur; prev = cur, cur = cur->next) {
if (cur == pkt)
break;
}
if (cur) {
if (prev)
prev->next = cur->next;
else
pkt->owner->packets = cur->next;
ast_mutex_unlock(&pkt->owner->lock);
free(cur);
pkt = NULL;
} else
ast_log(LOG_WARNING, "Weird, couldn't find packet owner!\n");
ast_mutex_unlock(&pkt->owner->lock);
Olle Johansson
committed
/*! \brief Transmit packet with retransmits
\return 0 on success, -1 on failure to allocate packet
*/
static int __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, char *data, int len, int fatal, int sipmethod)
int siptimer_a = DEFAULT_RETRANS;
if (!(pkt = ast_calloc(1, sizeof(*pkt) + len + 1)))
return -1;
memcpy(pkt->data, data, len);
pkt->method = sipmethod;
pkt->packetlen = len;
pkt->next = p->packets;
pkt->owner = p;
pkt->seqno = seqno;
pkt->flags = resp;
pkt->timer_t1 = p->timer_t1; /* Set SIP timer T1 */
if (pkt->timer_t1)
siptimer_a = pkt->timer_t1 * 2;
pkt->retransid = ast_sched_add_variable(sched, siptimer_a, retrans_pkt, pkt, 1);
Russell Bryant
committed
if (option_debug > 3 && sipdebug)
ast_log(LOG_DEBUG, "*** SIP TIMER: Initalizing retransmit timer on packet: Id #%d\n", pkt->retransid);
pkt->next = p->packets;
p->packets = pkt;
__sip_xmit(pkt->owner, pkt->data, pkt->packetlen); /* Send packet */
if (sipmethod == SIP_INVITE) {
/* Note this is a pending invite */
p->pendinginvite = seqno;
}
/*! \brief Kill a SIP dialog (called by scheduler) */
static int __sip_autodestruct(void *data)
{
struct sip_pvt *p = data;
/* If this is a subscription, tell the phone that we got a timeout */
if (p->subscribed) {
p->subscribed = TIMEOUT;
transmit_state_notify(p, AST_EXTENSION_DEACTIVATED, 1); /* Send last notification */
p->subscribed = NONE;
append_history(p, "Subscribestatus", "timeout");
if (option_debug > 2)
ast_log(LOG_DEBUG, "Re-scheduled destruction of SIP subsription %s\n", p->callid ? p->callid : "<unknown>");
return 10000; /* Reschedule this destruction so that we know that it's gone */
}
/* Reset schedule ID */
p->autokillid = -1;
if (option_debug)
ast_log(LOG_DEBUG, "Auto destroying call '%s'\n", p->callid);
Mark Spencer
committed
append_history(p, "AutoDestroy", "");
Olle Johansson
committed
ast_log(LOG_WARNING, "Autodestruct on dialog '%s' with owner in place (Method: %s)\n", p->callid, sip_methods[p->method].text);
Mark Spencer
committed
ast_queue_hangup(p->owner);
} else {
sip_destroy(p);
}
return 0;
}
/*! \brief Schedule destruction of SIP call */
static int sip_scheddestroy(struct sip_pvt *p, int ms)
{
if (sip_debug_test_pvt(p))
Olle Johansson
committed
ast_verbose("Scheduling destruction of SIP dialog '%s' in %d ms (Method: %s)\n", p->callid, ms, sip_methods[p->method].text);
Olle Johansson
committed
if (recordhistory)
append_history(p, "SchedDestroy", "%d ms", ms);
Kevin P. Fleming
committed
if (p->autokillid > -1)
ast_sched_del(sched, p->autokillid);
p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, p);
return 0;
}
/*! \brief Cancel destruction of SIP dialog */
static int sip_cancel_destroy(struct sip_pvt *p)
{
if (p->autokillid > -1)
ast_sched_del(sched, p->autokillid);
Mark Spencer
committed
append_history(p, "CancelDestroy", "");
/*! \brief Acknowledges receipt of a packet and stops retransmission */
static int __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod, int reset)
{
struct sip_pkt *cur, *prev = NULL;
int res = -1;
/* Just in case... */
Kevin P. Fleming
committed
msg = sip_methods[sipmethod].text;
for (cur = p->packets; cur; prev = cur, cur = cur->next) {
if ((cur->seqno == seqno) && ((ast_test_flag(cur, FLAG_RESPONSE)) == resp) &&
((ast_test_flag(cur, FLAG_RESPONSE)) ||
(!strncasecmp(msg, cur->data, strlen(msg)) && (cur->data[strlen(msg)] < 33)))) {
if (!resp && (seqno == p->pendinginvite)) {
ast_log(LOG_DEBUG, "Acked pending invite %d\n", p->pendinginvite);
p->pendinginvite = 0;
}
/* this is our baby */
if (prev)
prev->next = cur->next;
else
p->packets = cur->next;
if (cur->retransid > -1) {
Russell Bryant
committed
if (sipdebug && option_debug > 3)
ast_log(LOG_DEBUG, "** SIP TIMER: Cancelling retransmit of packet (reply received) Retransid #%d\n", cur->retransid);
if (option_debug)
ast_log(LOG_DEBUG, "Stopping retransmission on '%s' of %s %d: Match %s\n", p->callid, resp ? "Response" : "Request", seqno, res ? "Not Found" : "Found");
/*! \brief Pretend to ack all packets */
static int __sip_pretend_ack(struct sip_pvt *p)
{
struct sip_pkt *cur = NULL;
while (p->packets) {
ast_log(LOG_WARNING, "Have a packet that doesn't want to give up! %s\n", sip_methods[cur->method].text);
__sip_ack(p, p->packets->seqno, (ast_test_flag(p->packets, FLAG_RESPONSE)), cur->method, FALSE);
else { /* Unknown packet type */
char *c;
char method[128];
ast_copy_string(method, p->packets->data, sizeof(method));
c = ast_skip_blanks(method); /* XXX what ? */
*c = '\0';
__sip_ack(p, p->packets->seqno, (ast_test_flag(p->packets, FLAG_RESPONSE)), find_sip_method(method), FALSE);
}
return 0;
}
/*! \brief Acks receipt of packet, keep it around (used for provisional responses) */
static int __sip_semi_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod)
struct sip_pkt *cur;
for (cur = p->packets; cur; cur = cur->next) {
if (cur->seqno == seqno && ast_test_flag(cur, FLAG_RESPONSE) == resp &&
(ast_test_flag(cur, FLAG_RESPONSE) || method_match(sipmethod, cur->data))) {
if (cur->retransid > -1) {
Russell Bryant
committed
if (option_debug > 3 && sipdebug)
ast_log(LOG_DEBUG, "*** SIP TIMER: Cancelling retransmission #%d - %s (got response)\n", cur->retransid, sip_methods[sipmethod].text);
ast_sched_del(sched, cur->retransid);
cur->retransid = -1;
res = 0;
break;
}
}
if (option_debug)
ast_log(LOG_DEBUG, "(Provisional) Stopping retransmission (but retaining packet) on '%s' %s %d: %s\n", p->callid, resp ? "Response" : "Request", seqno, res ? "Not Found" : "Found");
/*! \brief Copy SIP request, parse it */
static void parse_copy(struct sip_request *dst, struct sip_request *src)
{
memset(dst, 0, sizeof(*dst));
memcpy(dst->data, src->data, sizeof(dst->data));
dst->len = src->len;
parse_request(dst);
/*! \brief Transmit response on SIP request*/
static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno)
if (sip_debug_test_pvt(p)) {
Olle Johansson
committed
char iabuf[INET_ADDRSTRLEN];
if (ast_test_flag(&p->flags[0], SIP_NAT_ROUTE))
ast_verbose("%sTransmitting (NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr), ntohs(p->recv.sin_port), req->data);
ast_verbose("%sTransmitting (no NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), req->data);
Olle Johansson
committed
if (recordhistory) {
struct sip_request tmp;
parse_copy(&tmp, req);
append_history(p, reliable ? "TxRespRel" : "TxResp", "%s / %s - %s", tmp.data, get_header(&tmp, "CSeq"),
tmp.method == SIP_RESPONSE ? tmp.rlPart2 : sip_methods[tmp.method].text);
Mark Spencer
committed
}
Olle Johansson
committed
res = (reliable) ?
__sip_reliable_xmit(p, seqno, 1, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) :
Olle Johansson
committed
__sip_xmit(p, req->data, req->len);
/*! \brief Send SIP Request to the other part of the dialogue */
static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno)
if (sip_debug_test_pvt(p)) {
Olle Johansson
committed
char iabuf[INET_ADDRSTRLEN];
if (ast_test_flag(&p->flags[0], SIP_NAT_ROUTE))
ast_verbose("%sTransmitting (NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr), ntohs(p->recv.sin_port), req->data);
ast_verbose("%sTransmitting (no NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), req->data);
Olle Johansson
committed
if (recordhistory) {
struct sip_request tmp;
parse_copy(&tmp, req);
append_history(p, reliable ? "TxReqRel" : "TxReq", "%s / %s - %s", tmp.data, get_header(&tmp, "CSeq"), sip_methods[tmp.method].text);
Mark Spencer
committed
}
Olle Johansson
committed
res = (reliable) ?
__sip_reliable_xmit(p, seqno, 0, req->data, req->len, (reliable > 1), req->method) :
__sip_xmit(p, req->data, req->len);
/*! \brief Pick out text in brackets from character string
\return pointer to terminated stripped string
\param tmp input string that will be modified */
Kevin P. Fleming
committed
static char *get_in_brackets(char *tmp)
Kevin P. Fleming
committed
char *parse;
char *first_quote;
char *first_bracket;
char *second_bracket;
char last_char;
parse = tmp;
Kevin P. Fleming
committed
first_quote = strchr(parse, '"');
first_bracket = strchr(parse, '<');
if (first_quote && first_bracket && (first_quote < first_bracket)) {
last_char = '\0';
for (parse = first_quote + 1; *parse; parse++) {
if ((*parse == '"') && (last_char != '\\'))
break;
last_char = *parse;
}
if (!*parse) {
ast_log(LOG_WARNING, "No closing quote found in '%s'\n", tmp);
return tmp;
}
parse++;
continue;
Kevin P. Fleming
committed
if (first_bracket) {
second_bracket = strchr(first_bracket + 1, '>');
if (second_bracket) {
*second_bracket = '\0';
return first_bracket + 1;
} else {
ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", tmp);
return tmp;
}
}
return tmp;
/*! \brief Send SIP MESSAGE text within a call
Called from PBX core sendtext() application */
Mark Spencer
committed
static int sip_sendtext(struct ast_channel *ast, const char *text)
struct sip_pvt *p = ast->tech_pvt;
int debug = sip_debug_test_pvt(p);
ast_verbose("Sending text %s on %s\n", text, ast->name);
if (!p)
return -1;
if (ast_strlen_zero(text))
ast_verbose("Really sending text %s on %s\n", text, ast->name);
transmit_message_with_text(p, text);
return 0;
/*! \brief Update peer object in realtime storage */
Mark Spencer
committed
static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, const char *username, const char *fullcontact, int expirey)
char port[10];
char ipaddr[20];
char regseconds[20];
time_t nowtime;
const char *fc = fullcontact ? "fullcontact" : NULL;
time(&nowtime);
nowtime += expirey;
snprintf(regseconds, sizeof(regseconds), "%d", (int)nowtime); /* Expiration time */
ast_inet_ntoa(ipaddr, sizeof(ipaddr), sin->sin_addr);
snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port));
ast_update_realtime("sippeers", "name", peername, "ipaddr", ipaddr,
"port", port, "regseconds", regseconds,
"username", username, fc, fullcontact, NULL); /* note fc _can_ be NULL */
/*! \brief Automatically add peer extension to dial plan */
static void register_peer_exten(struct sip_peer *peer, int onoff)
char multi[256];
Olle Johansson
committed
if (!ast_strlen_zero(global_regcontext)) {
ast_copy_string(multi, S_OR(peer->regexten, peer->name), sizeof(multi));
stringp = multi;
while((ext = strsep(&stringp, "&"))) {
if (onoff)
Olle Johansson
committed
ast_add_extension(global_regcontext, 1, ext, 1, NULL, NULL, "Noop",
ast_strdup(peer->name), free, "SIP");
Olle Johansson
committed
ast_context_remove_extension(global_regcontext, ext, 1, NULL);
Mark Spencer
committed
}
}
/*! \brief Destroy peer object from memory */
Mark Spencer
committed
static void sip_destroy_peer(struct sip_peer *peer)
Olle Johansson
committed
if (option_debug > 2)
ast_log(LOG_DEBUG, "Destroying SIP peer %s\n", peer->name);
/* Delete it, it needs to disappear */
if (peer->call)
sip_destroy(peer->call);
Kevin P. Fleming
committed
if (peer->mwipvt) { /* We have an active subscription, delete it */
sip_destroy(peer->mwipvt);
}
ast_variables_destroy(peer->chanvars);
peer->chanvars = NULL;
}
if (peer->expire > -1)
ast_sched_del(sched, peer->expire);
if (peer->pokeexpire > -1)
ast_sched_del(sched, peer->pokeexpire);
Olle Johansson
committed
register_peer_exten(peer, FALSE);
if (ast_test_flag(&peer->flags[1], SIP_PAGE2_SELFDESTRUCT))
Mark Spencer
committed
apeerobjs--;
else if (ast_test_flag(&peer->flags[0], SIP_REALTIME))
Mark Spencer
committed
rpeerobjs--;
else
speerobjs--;
clear_realm_authentication(peer->auth);
peer->auth = NULL;
if (peer->dnsmgr)
ast_dnsmgr_release(peer->dnsmgr);
/*! \brief Update peer data in database (if used) */
static void update_peer(struct sip_peer *p, int expiry)
int rtcachefriends = ast_test_flag(&p->flags[1], SIP_PAGE2_RTCACHEFRIENDS);
if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTUPDATE) &&
(ast_test_flag(&p->flags[0], SIP_REALTIME) || rtcachefriends)) {
Mark Spencer
committed
realtime_update_peer(p->name, &p->addr, p->username, rtcachefriends ? p->fullcontact : NULL, expiry);
/*! \brief realtime_peer: Get peer from realtime storage
* Checks the "sippeers" realtime family from extconfig.conf
* \todo Consider adding check of port address when matching here to follow the same
* algorithm as for static peers. Will we break anything by adding that?
*/
static struct sip_peer *realtime_peer(const char *peername, struct sockaddr_in *sin)
{
struct sip_peer *peer = NULL;
struct ast_variable *var;
struct ast_variable *tmp;
char *newpeername = (char *) peername;
if (newpeername)
var = ast_load_realtime("sippeers", "name", peername, NULL);
else if (sin) { /* Then check on IP address for dynamic peers */
ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr);
var = ast_load_realtime("sippeers", "host", iabuf, NULL); /* First check for fixed IP hosts */
if (!var)
var = ast_load_realtime("sippeers", "ipaddr", iabuf, NULL); /* Then check for registred hosts */
} else
return NULL;
if (!var)
return NULL;
for (tmp = var; tmp; tmp = tmp->next) {
/* If this is type=user, then skip this object. */
if (!strcasecmp(tmp->name, "type") &&
!strcasecmp(tmp->value, "user")) {
} else if (!newpeername && !strcasecmp(tmp->name, "name")) {
newpeername = tmp->value;
Mark Spencer
committed
}
if (!newpeername) { /* Did not find peer in realtime */
ast_log(LOG_WARNING, "Cannot Determine peer name ip=%s\n", iabuf);
ast_variables_destroy(var);
return NULL;
Mark Spencer
committed
/* Peer found in realtime, now build it in memory */
peer = build_peer(newpeername, var, !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS));
if (!peer) {
ast_variables_destroy(var);
return NULL;
Mark Spencer
committed
if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
ast_copy_flags(&peer->flags[1],&global_flags[1], SIP_PAGE2_RTAUTOCLEAR|SIP_PAGE2_RTCACHEFRIENDS);
if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
if (peer->expire > -1) {
ast_sched_del(sched, peer->expire);
peer->expire = ast_sched_add(sched, (global_rtautoclear) * 1000, expire_register, (void *)peer);
ASTOBJ_CONTAINER_LINK(&peerl,peer);
} else {
ast_set_flag(&peer->flags[0], SIP_REALTIME);
Mark Spencer
committed
/*! \brief Support routine for find_peer */
Mark Spencer
committed
static int sip_addrcmp(char *name, struct sockaddr_in *sin)
{
/* We know name is the first field, so we can cast */
struct sip_peer *p = (struct sip_peer *) name;
return !(!inaddrcmp(&p->addr, sin) ||
(ast_test_flag(&p->flags[0], SIP_INSECURE_PORT) &&
Mark Spencer
committed
(p->addr.sin_addr.s_addr == sin->sin_addr.s_addr)));
}
/*! \brief Locate peer by name or ip address
* This is used on incoming SIP message to find matching peer on ip
or outgoing message to find matching peer on name */
static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime)
{
struct sip_peer *p = NULL;
Mark Spencer
committed
if (peer)
p = ASTOBJ_CONTAINER_FIND(&peerl, peer);
Mark Spencer
committed
else
p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp);
/*! \brief Remove user object from in-memory storage */
Mark Spencer
committed
static void sip_destroy_user(struct sip_user *user)
Olle Johansson
committed
if (option_debug > 2)
ast_log(LOG_DEBUG, "Destroying user object from memory: %s\n", user->name);
ast_variables_destroy(user->chanvars);
user->chanvars = NULL;
if (ast_test_flag(&user->flags[0], SIP_REALTIME))
Mark Spencer
committed
ruserobjs--;
else
suserobjs--;
/*! \brief Load user from realtime storage
* Loads user from "sipusers" category in realtime (extconfig.conf)
* Users are matched on From: user name (the domain in skipped) */
static struct sip_user *realtime_user(const char *username)
{
struct ast_variable *var;
struct ast_variable *tmp;
struct sip_user *user = NULL;
var = ast_load_realtime("sipusers", "name", username, NULL);
if (!var)
return NULL;
for (tmp = var; tmp; tmp = tmp->next) {
!strcasecmp(tmp->value, "peer")) {
user = build_user(username, var, !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS));
if (!user) { /* No user found */
ast_variables_destroy(var);
return NULL;
}
if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
ast_set_flag(&user->flags[1], SIP_PAGE2_RTCACHEFRIENDS);
ASTOBJ_CONTAINER_LINK(&userl,user);
} else {
/* Move counter from s to r... */
suserobjs--;
ruserobjs++;
ast_set_flag(&user->flags[0], SIP_REALTIME);
Mark Spencer
committed
}
/*! \brief Locate user by name
* Locates user by name (From: sip uri user name part) first
* from in-memory list (static configuration) then from
* realtime storage (defined in extconfig.conf) */
static struct sip_user *find_user(const char *name, int realtime)
struct sip_user *u = ASTOBJ_CONTAINER_FIND(&userl, name);
if (!u && realtime)
/*! \brief Create address structure from peer reference */
static int create_addr_from_peer(struct sip_pvt *r, struct sip_peer *peer)
{
if ((peer->addr.sin_addr.s_addr || peer->defaddr.sin_addr.s_addr) &&
(!peer->maxms || ((peer->lastms >= 0) && (peer->lastms <= peer->maxms)))) {
r->sa = (peer->addr.sin_addr.s_addr) ? peer->addr : peer->defaddr;
r->recv = r->sa;
} else {
return -1;
}
ast_copy_flags(&r->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
ast_copy_flags(&r->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
r->capability = peer->capability;
if (!ast_test_flag(&r->flags[1], SIP_PAGE2_VIDEOSUPPORT) && r->vrtp) {
Kevin P. Fleming
committed
r->prefs = peer->prefs;
natflags = ast_test_flag(&r->flags[0], SIP_NAT) & SIP_NAT_ROUTE;
if (r->rtp) {
if (option_debug)
ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", natflags);
ast_rtp_setnat(r->rtp, natflags);
}
if (r->vrtp) {
if (option_debug)
ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", natflags);
ast_rtp_setnat(r->vrtp, natflags);
ast_string_field_set(r, peername, peer->username);
ast_string_field_set(r, authname, peer->username);
ast_string_field_set(r, username, peer->username);
ast_string_field_set(r, peersecret, peer->secret);
ast_string_field_set(r, peermd5secret, peer->md5secret);
ast_string_field_set(r, tohost, peer->tohost);
ast_string_field_set(r, fullcontact, peer->fullcontact);
if (!r->initreq.headers && !ast_strlen_zero(peer->fromdomain)) {
char *tmpcall;
char *c;
tmpcall = ast_strdupa(r->callid);
Russell Bryant
committed
if (tmpcall) {
c = strchr(tmpcall, '@');
if (c) {
*c = '\0';
ast_string_field_build(r, callid, "%s@%s", tmpcall, peer->fromdomain);
}
}
}
if (ast_strlen_zero(r->tohost)) {
char iabuf[INET_ADDRSTRLEN];
ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr.s_addr ? peer->addr.sin_addr : peer->defaddr.sin_addr);
ast_string_field_set(r, tohost, iabuf);
}
if (!ast_strlen_zero(peer->fromdomain))
ast_string_field_set(r, fromdomain, peer->fromdomain);
if (!ast_strlen_zero(peer->fromuser))
ast_string_field_set(r, fromuser, peer->fromuser);
r->maxtime = peer->maxms;
r->callgroup = peer->callgroup;
r->pickupgroup = peer->pickupgroup;
/* Set timer T1 to RTT for this peer (if known by qualify=) */
/* Minimum is settable or default to 100 ms */
if (peer->maxms && peer->lastms)
r->timer_t1 = peer->lastms < global_t1min ? global_t1min : peer->lastms;
if ((ast_test_flag(&r->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
(ast_test_flag(&r->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
r->noncodeccapability |= AST_RTP_DTMF;
else
r->noncodeccapability &= ~AST_RTP_DTMF;
ast_string_field_set(r, context, peer->context);
r->rtptimeout = peer->rtptimeout;
r->rtpholdtimeout = peer->rtpholdtimeout;
r->rtpkeepalive = peer->rtpkeepalive;
Kevin P. Fleming
committed
if (peer->call_limit)
ast_set_flag(&r->flags[0], SIP_CALL_LIMIT);
return 0;
}
/*! \brief create address structure from peer name
* Or, if peer not found, find it in the global DNS
* returns TRUE (-1) on failure, FALSE on success */
static int create_addr(struct sip_pvt *dialog, const char *opeer)