Newer
Older
if (sipmethod == SIP_INVITE) {
/* Note this is a pending invite */
p->pendinginvite = seqno;
}
Olle Johansson
committed
return AST_SUCCESS;
/*! \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) {
Olle Johansson
committed
transmit_state_notify(p, AST_EXTENSION_DEACTIVATED, 1, TRUE); /* 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 */
}
Olle Johansson
committed
if (p->subscribed == MWI_NOTIFICATION)
if (p->relatedpeer)
unref_peer(p->relatedpeer); /* Remove link to peer. If it's realtime, make sure it's gone from memory) */
Olle Johansson
committed
/* Reset schedule ID */
p->autokillid = -1;
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 if (p->refer) {
if (option_debug > 2)
ast_log(LOG_DEBUG, "Finally hanging up channel after transfer: %s\n", p->callid);
transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1);
append_history(p, "ReferBYE", "Sending BYE on transferer call leg %s", p->callid);
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
} else {
append_history(p, "AutoDestroy", "%s", p->callid);
if (option_debug)
ast_log(LOG_DEBUG, "Auto destroying SIP dialog '%s'\n", p->callid);
sip_destroy(p); /* Go ahead and destroy dialog. All attempts to recover is done */
}
Olle Johansson
committed
static void sip_scheddestroy(struct sip_pvt *p, int ms)
if (ms < 0) {
if (p->timer_t1 == 0)
p->timer_t1 = SIP_TIMER_T1; /* Set timer T1 if not set (RFC 3261) */
ms = p->timer_t1 * 64;
}
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);
if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY))
Olle Johansson
committed
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);
}
/*! \brief Cancel destruction of SIP dialog */
Olle Johansson
committed
static void sip_cancel_destroy(struct sip_pvt *p)
if (p->autokillid > -1) {
append_history(p, "CancelDestroy", "");
p->autokillid = -1;
}
/*! \brief Acknowledges receipt of a packet and stops retransmission */
Olle Johansson
committed
static void __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod)
const char *msg = "Not Found"; /* used only for debugging */
Kevin P. Fleming
committed
sip_pvt_lock(p);
for (cur = p->packets; cur; prev = cur, cur = cur->next) {
if (cur->seqno != seqno || ast_test_flag(cur, FLAG_RESPONSE) != resp)
continue;
if (ast_test_flag(cur, FLAG_RESPONSE) || cur->method == sipmethod) {
msg = "Found";
if (!resp && (seqno == p->pendinginvite)) {
if (option_debug)
ast_log(LOG_DEBUG, "Acked pending invite %d\n", p->pendinginvite);
p->pendinginvite = 0;
}
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);
UNLINK(cur, p->packets, prev);
Olle Johansson
committed
free(cur);
sip_pvt_unlock(p);
if (option_debug)
ast_log(LOG_DEBUG, "Stopping retransmission on '%s' of %s %d: Match %s\n",
p->callid, resp ? "Response" : "Request", seqno, msg);
Olle Johansson
committed
/*! \brief Pretend to ack all packets
* maybe the lock on p is not strictly necessary but there might be a race */
static void __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);
Olle Johansson
committed
return;
method = (cur->method) ? cur->method : find_sip_method(cur->data);
Olle Johansson
committed
__sip_ack(p, cur->seqno, ast_test_flag(cur, FLAG_RESPONSE), method);
}
}
/*! \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);
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, const struct sip_request *src)
{
memset(dst, 0, sizeof(*dst));
memcpy(dst->data, src->data, sizeof(dst->data));
dst->len = src->len;
parse_request(dst);
static void add_blank(struct sip_request *req)
{
if (!req->lines) {
/* Add extra empty return. add_header() reserves 4 bytes so cannot be truncated */
snprintf(req->data + req->len, sizeof(req->data) - req->len, "\r\n");
req->len += strlen(req->data + req->len);
}
}
/*! \brief Transmit response on SIP request*/
static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno)
add_blank(req);
if (sip_debug_test_pvt(p)) {
const struct sockaddr_in *dst = sip_real_dst(p);
ast_verbose("\n<--- %sTransmitting (%s) to %s:%d --->\n%s\n<------------>\n",
reliable ? "Reliably " : "", sip_nat_mode(p),
Russell Bryant
committed
ast_inet_ntoa(dst->sin_addr),
ntohs(dst->sin_port), req->data);
if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) {
Olle Johansson
committed
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.method == SIP_UNKNOWN) ? 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)
add_blank(req);
if (sip_debug_test_pvt(p)) {
if (ast_test_flag(&p->flags[0], SIP_NAT_ROUTE))
Russell Bryant
committed
ast_verbose("%sTransmitting (NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port), req->data);
Russell Bryant
committed
ast_verbose("%sTransmitting (no NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port), req->data);
if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) {
Olle Johansson
committed
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 Locate closing quote in a string, skipping escaped quotes.
* optionally with a limit on the search.
* start must be past the first quote.
*/
static const char *find_closing_quote(const char *start, const char *lim)
{
char last_char = '\0';
const char *s;
for (s = start; *s && s != lim; last_char = *s++) {
if (*s == '"' && last_char != '\\')
break;
}
return s;
}
/*! \brief Pick out text in brackets from character string
\return pointer to terminated stripped string
\param tmp input string that will be modified
Examples:
"foo" <bar> valid input, returns bar
foo returns the whole string
< "foo ... > returns the string between brackets
< "foo... bogus (missing closing bracket), returns the whole string
XXX maybe should still skip the opening bracket
*/
Kevin P. Fleming
committed
static char *get_in_brackets(char *tmp)
const char *parse = tmp;
Kevin P. Fleming
committed
char *first_bracket;
/*
* Skip any quoted text until we find the part in brackets.
* On any error give up and return the full string.
*/
while ( (first_bracket = strchr(parse, '<')) ) {
char *first_quote = strchr(parse, '"');
if (!first_quote || first_quote > first_bracket)
break; /* no need to look at quoted part */
/* the bracket is within quotes, so ignore it */
parse = find_closing_quote(first_quote + 1, NULL);
if (!*parse) { /* not found, return full string ? */
/* XXX or be robust and return in-bracket part ? */
ast_log(LOG_WARNING, "No closing quote found in '%s'\n", tmp);
break;
parse++;
}
if (first_bracket) {
char *second_bracket = strchr(first_bracket + 1, '>');
if (second_bracket) {
*second_bracket = '\0';
tmp = first_bracket + 1;
} else {
ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", tmp);
Kevin P. Fleming
committed
}
/*!
* parses a URI in its components.
* If scheme is specified, drop it from the top.
* If a component is not requested, do not split around it.
* This means that if we don't have domain, we cannot split
* name:pass and domain:port.
* It is safe to call with ret_name, pass, domain, port
* pointing all to the same place.
* Init pointers to empty string so we never get NULL dereferencing.
* Overwrites the string.
* return 0 on success, other values on error.
*/
static int parse_uri(char *uri, char *scheme,
char **ret_name, char **pass, char **domain, char **port, char **options)
{
char *name = NULL;
int error = 0;
/* init field as required */
name = strsep(&uri, ";"); /* remove options */
if (scheme) {
int l = strlen(scheme);
if (!strncmp(name, scheme, l))
name += l;
else {
ast_log(LOG_NOTICE, "Missing scheme '%s' in '%s'\n", scheme, name);
error = -1;
}
}
if (!domain) {
/* if we don't want to split around domain, keep everything as a name,
* so we need to do nothing here, except remember why.
*/
} else {
/* store the result in a temp. variable to avoid it being
* overwritten if arguments point to the same place.
*/
char *c, *dom = "";
if ((c = strchr(name, '@')) == NULL) {
/* domain-only URI, according to the SIP RFC. */
dom = name;
*c++ = '\0';
dom = c;
}
if (port && (c = strchr(dom, ':'))) { /* Remove :port */
*c++ = '\0';
*port = c;
}
if (pass && (c = strchr(name, ':'))) { /* user:password */
*c++ = '\0';
*pass = c;
}
*domain = dom;
}
if (ret_name) /* same as for domain, store the result only at the end */
*ret_name = name;
if (options)
*options = uri ? uri : "";
return error;
}
/*! \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
If the Asterisk system name is set in asterisk.conf, we will use
that name and store that in the "regserver" field in the sippeers
table to facilitate multi-server setups.
*/
Mark Spencer
committed
static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, const char *username, const char *fullcontact, int expirey)
Russell Bryant
committed
char ipaddr[INET_ADDRSTRLEN];
char *sysname = ast_config_AST_SYSTEM_NAME;
char *syslabel = NULL;
time_t nowtime = time(NULL) + expirey;
const char *fc = fullcontact ? "fullcontact" : NULL;
snprintf(regseconds, sizeof(regseconds), "%d", (int)nowtime); /* Expiration time */
Russell Bryant
committed
ast_copy_string(ipaddr, ast_inet_ntoa(sin->sin_addr), sizeof(ipaddr));
snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port));
if (ast_strlen_zero(sysname)) /* No system name, disable this */
sysname = NULL;
else if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTSAVE_SYSNAME))
syslabel = "regserver";
if (fc)
ast_update_realtime("sippeers", "name", peername, "ipaddr", ipaddr,
"port", port, "regseconds", regseconds,
"username", username, fc, fullcontact, syslabel, sysname, NULL); /* note fc and syslabel _can_ be NULL */
else
ast_update_realtime("sippeers", "name", peername, "ipaddr", ipaddr,
"port", port, "regseconds", regseconds,
"username", username, syslabel, sysname, NULL); /* note syslabel _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];
char *stringp, *ext, *context;
/* XXX note that global_regcontext is both a global 'enable' flag and
* the name of the global regexten context, if not specified
* individually.
*/
if (ast_strlen_zero(global_regcontext))
return;
ast_copy_string(multi, S_OR(peer->regexten, peer->name), sizeof(multi));
stringp = multi;
while ((ext = strsep(&stringp, "&"))) {
if ((context = strchr(ext, '@'))) {
*context++ = '\0'; /* split ext@context */
if (!ast_context_find(context)) {
ast_log(LOG_WARNING, "Context %s must exist in regcontext= in sip.conf!\n", context);
continue;
Mark Spencer
committed
}
if (onoff)
ast_add_extension(context, 1, ext, 1, NULL, NULL, "Noop",
ast_strdup(peer->name), ast_free, "SIP");
else
ast_context_remove_extension(context, ext, 1, NULL);
}
/*! \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 */
Kevin P. Fleming
committed
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--;
if (option_debug > 2)
ast_log(LOG_DEBUG,"-REALTIME- peer Destroyed. Name: %s. Realtime Peer objects: %d\n", peer->name, rpeerobjs);
} else
Mark Spencer
committed
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 *newpeername, struct sockaddr_in *sin)
struct sip_peer *peer;
struct ast_variable *var = NULL;
Russell Bryant
committed
char ipaddr[INET_ADDRSTRLEN];
if (newpeername)
var = ast_load_realtime("sippeers", "name", newpeername, NULL);
else if (sin) { /* Then check on IP address for dynamic peers */
Russell Bryant
committed
ast_copy_string(ipaddr, ast_inet_ntoa(sin->sin_addr), sizeof(ipaddr));
var = ast_load_realtime("sippeers", "host", ipaddr, NULL); /* First check for fixed IP hosts */
if (!var)
Russell Bryant
committed
var = ast_load_realtime("sippeers", "ipaddr", ipaddr, NULL); /* Then check for registred hosts */
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 */
Russell Bryant
committed
ast_log(LOG_WARNING, "Cannot Determine peer name ip=%s\n", ipaddr);
return NULL;
Mark Spencer
committed
/* Peer found in realtime, now build it in memory */
peer = build_peer(newpeername, var, NULL, !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS));
if (!peer) {
ast_variables_destroy(var);
return NULL;
Mark Spencer
committed
if (option_debug > 2)
ast_log(LOG_DEBUG,"-REALTIME- loading peer from database to memory. Name: %s. Peer objects: %d\n", peer->name, rpeerobjs);
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)
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
/*! \brief Set nat mode on the various data sockets */
static void do_setnat(struct sip_pvt *p, int natflags)
{
const char *mode = natflags ? "On" : "Off";
if (p->rtp) {
if (option_debug)
ast_log(LOG_DEBUG, "Setting NAT on RTP to %s\n", mode);
ast_rtp_setnat(p->rtp, natflags);
}
if (p->vrtp) {
if (option_debug)
ast_log(LOG_DEBUG, "Setting NAT on VRTP to %s\n", mode);
ast_rtp_setnat(p->vrtp, natflags);
}
if (p->udptl) {
if (option_debug)
ast_log(LOG_DEBUG, "Setting NAT on UDPTL to %s\n", mode);
ast_udptl_setnat(p->udptl, natflags);
}
}
/*! \brief Create address structure from peer reference.
* return -1 on error, 0 on success.
*/
Olle Johansson
committed
static int create_addr_from_peer(struct sip_pvt *dialog, 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)))) {
Olle Johansson
committed
dialog->sa = (peer->addr.sin_addr.s_addr) ? peer->addr : peer->defaddr;
dialog->recv = dialog->sa;
} else
return -1;
Olle Johansson
committed
ast_copy_flags(&dialog->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
ast_copy_flags(&dialog->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
dialog->capability = peer->capability;
if ((!ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) || !(dialog->capability & AST_FORMAT_VIDEO_MASK)) && dialog->vrtp) {
Olle Johansson
committed
ast_rtp_destroy(dialog->vrtp);
dialog->vrtp = NULL;
Olle Johansson
committed
dialog->prefs = peer->prefs;
if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_T38SUPPORT)) {
dialog->t38.capability = global_t38_capability;
if (dialog->udptl) {
if (ast_udptl_get_error_correction_scheme(dialog->udptl) == UDPTL_ERROR_CORRECTION_FEC )
dialog->t38.capability |= T38FAX_UDP_EC_FEC;
else if (ast_udptl_get_error_correction_scheme(dialog->udptl) == UDPTL_ERROR_CORRECTION_REDUNDANCY )
dialog->t38.capability |= T38FAX_UDP_EC_REDUNDANCY;
else if (ast_udptl_get_error_correction_scheme(dialog->udptl) == UDPTL_ERROR_CORRECTION_NONE )
dialog->t38.capability |= T38FAX_UDP_EC_NONE;
dialog->t38.capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
if (option_debug > 1)
Olle Johansson
committed
ast_log(LOG_DEBUG,"Our T38 capability (%d)\n", dialog->t38.capability);
Olle Johansson
committed
dialog->t38.jointcapability = dialog->t38.capability;
} else if (dialog->udptl) {
ast_udptl_destroy(dialog->udptl);
dialog->udptl = NULL;
do_setnat(dialog, ast_test_flag(&dialog->flags[0], SIP_NAT) & SIP_NAT_ROUTE );
Olle Johansson
committed
if (dialog->rtp) {
ast_rtp_setdtmf(dialog->rtp, ast_test_flag(&dialog->flags[0], SIP_DTMF) != SIP_DTMF_INFO);
ast_rtp_setdtmfcompensate(dialog->rtp, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
ast_rtp_set_rtptimeout(dialog->rtp, peer->rtptimeout);
ast_rtp_set_rtpholdtimeout(dialog->rtp, peer->rtpholdtimeout);
ast_rtp_set_rtpkeepalive(dialog->rtp, peer->rtpkeepalive);
/* Set Frame packetization */
ast_rtp_codec_setpref(dialog->rtp, &dialog->prefs);
dialog->autoframing = peer->autoframing;
Olle Johansson
committed
if (dialog->vrtp) {
ast_rtp_setdtmf(dialog->vrtp, 0);
ast_rtp_setdtmfcompensate(dialog->vrtp, 0);
ast_rtp_set_rtptimeout(dialog->vrtp, peer->rtptimeout);
ast_rtp_set_rtpholdtimeout(dialog->vrtp, peer->rtpholdtimeout);
ast_rtp_set_rtpkeepalive(dialog->vrtp, peer->rtpkeepalive);
Olle Johansson
committed
ast_string_field_set(dialog, peername, peer->username);
ast_string_field_set(dialog, authname, peer->username);
ast_string_field_set(dialog, username, peer->username);
ast_string_field_set(dialog, peersecret, peer->secret);
ast_string_field_set(dialog, peermd5secret, peer->md5secret);
ast_string_field_set(dialog, tohost, peer->tohost);
ast_string_field_set(dialog, fullcontact, peer->fullcontact);
if (!dialog->initreq.headers && !ast_strlen_zero(peer->fromdomain)) {
char *tmpcall;
char *c;
Olle Johansson
committed
tmpcall = ast_strdupa(dialog->callid);
Russell Bryant
committed
c = strchr(tmpcall, '@');
if (c) {
*c = '\0';
Olle Johansson
committed
ast_string_field_build(dialog, callid, "%s@%s", tmpcall, peer->fromdomain);
}
}
Olle Johansson
committed
if (ast_strlen_zero(dialog->tohost))
ast_string_field_set(dialog, tohost, ast_inet_ntoa(dialog->sa.sin_addr));
if (!ast_strlen_zero(peer->fromdomain))
Olle Johansson
committed
ast_string_field_set(dialog, fromdomain, peer->fromdomain);
if (!ast_strlen_zero(peer->fromuser))
Olle Johansson
committed
ast_string_field_set(dialog, fromuser, peer->fromuser);
dialog->callgroup = peer->callgroup;
dialog->pickupgroup = peer->pickupgroup;
dialog->allowtransfer = peer->allowtransfer;
/* 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)
Olle Johansson
committed
dialog->timer_t1 = peer->lastms < global_t1min ? global_t1min : peer->lastms;
if ((ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
(ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
dialog->noncodeccapability |= AST_RTP_DTMF;
Olle Johansson
committed
dialog->noncodeccapability &= ~AST_RTP_DTMF;
ast_string_field_set(dialog, context, peer->context);
dialog->rtptimeout = peer->rtptimeout;
Kevin P. Fleming
committed
if (peer->call_limit)
Olle Johansson
committed
ast_set_flag(&dialog->flags[0], SIP_CALL_LIMIT);
dialog->maxcallbitrate = peer->maxcallbitrate;
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)
int portno;
char host[MAXHOSTNAMELEN], *hostn;
ast_copy_string(peername, opeer, sizeof(peername));
port = strchr(peername, ':');
if (port)
*port++ = '\0';
dialog->sa.sin_family = AF_INET;
dialog->timer_t1 = SIP_TIMER_T1; /* Default SIP retransmission timer T1 (RFC 3261) */
if (peer) {
int res = create_addr_from_peer(dialog, peer);
unref_peer(peer);
portno = port ? atoi(port) : STANDARD_SIP_PORT;
if (global_srvlookup) {
char service[MAXHOSTNAMELEN];
int tportno;
int ret;
snprintf(service, sizeof(service), "_sip._udp.%s", peername);
ret = ast_get_srv(NULL, host, sizeof(host), &tportno, service);
if (ret > 0) {
hostn = host;
portno = tportno;
ast_log(LOG_WARNING, "No such host: %s\n", peername);
memcpy(&dialog->sa.sin_addr, hp->h_addr, sizeof(dialog->sa.sin_addr));
dialog->sa.sin_port = htons(portno);
dialog->recv = dialog->sa;
return 0;
/*! \brief Scheduled congestion on a call */
static int auto_congest(void *nothing)
{
struct sip_pvt *p = nothing;
sip_pvt_lock(p);
/* XXX fails on possible deadlock */
ast_log(LOG_NOTICE, "Auto-congesting %s\n", p->owner->name);
append_history(p, "Cong", "Auto-congesting (timer)");
Mark Spencer
committed
ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
sip_pvt_unlock(p);
/*! \brief Initiate SIP call from PBX
* used from the dial() application */
static int sip_call(struct ast_channel *ast, char *dest, int timeout)
{
int res;
struct sip_pvt *p;
struct varshead *headp;
struct ast_var_t *current;
const char *referer = NULL; /* SIP referrer */
p = ast->tech_pvt;
if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
ast_log(LOG_WARNING, "sip_call called on %s, neither down nor reserved\n", ast->name);
return -1;
}
Kevin P. Fleming
committed
/* Check whether there is vxml_url, distinctive ring variables */
headp=&ast->varshead;
AST_LIST_TRAVERSE(headp,current,entries) {
/* Check whether there is a VXML_URL variable */
Kevin P. Fleming
committed
if (!p->options->vxml_url && !strcasecmp(ast_var_name(current), "VXML_URL")) {
p->options->vxml_url = ast_var_value(current);
} else if (!p->options->uri_options && !strcasecmp(ast_var_name(current), "SIP_URI_OPTIONS")) {
p->options->uri_options = ast_var_value(current);
Kevin P. Fleming
committed
} else if (!p->options->addsipheaders && !strncasecmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) {
/* Check whether there is a variable with a name starting with SIPADDHEADER */
Kevin P. Fleming
committed
p->options->addsipheaders = 1;
} else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER")) {
/* This is a transfered call */
p->options->transfer = 1;
} else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REFERER")) {
referer = ast_var_value(current);
} else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REPLACES")) {
/* We're replacing a call. */
p->options->replaces = ast_var_value(current);
} else if (!strcasecmp(ast_var_name(current), "T38CALL")) {
p->t38.state = T38_LOCAL_DIRECT;
if (option_debug)
ast_log(LOG_DEBUG,"T38State change to %d on channel %s\n", p->t38.state, ast->name);
ast_set_flag(&p->flags[0], SIP_OUTGOING);
if (p->options->transfer) {
char buf[BUFSIZ/2];
if (referer) {
if (sipdebug && option_debug > 2)
ast_log(LOG_DEBUG, "Call for %s transfered by %s\n", p->username, referer);
snprintf(buf, sizeof(buf)-1, "-> %s (via %s)", p->cid_name, referer);
snprintf(buf, sizeof(buf)-1, "-> %s", p->cid_name);
ast_string_field_set(p, cid_name, buf);
}
if (option_debug)
ast_log(LOG_DEBUG, "Outgoing Call for %s\n", p->username);
res = update_call_counter(p, INC_CALL_RINGING);
Olle Johansson
committed
if (res == -1)
return res;
p->callingpres = ast->cid.cid_pres;
p->jointcapability = ast_translate_available_formats(p->capability, p->prefcodec);
/* If there are no audio formats left to offer, punt */
if (!(p->jointcapability & AST_FORMAT_AUDIO_MASK)) {
ast_log(LOG_WARNING, "No audio format found to offer. Cancelling call to %s\n", p->username);
res = -1;
} else {
p->t38.jointcapability = p->t38.capability;
if (option_debug > 1)
ast_log(LOG_DEBUG,"Our T38 capability (%d), joint T38 capability (%d)\n", p->t38.capability, p->t38.jointcapability);
transmit_invite(p, SIP_INVITE, 1, 2);
Olle Johansson
committed
p->invitestate = INV_CALLING;
/* Initialize auto-congest time */
p->initid = ast_sched_add(sched, SIP_TRANS_TIMEOUT, auto_congest, p);
/*! \brief Destroy registry object
Objects created with the register= statement in static configuration */
Mark Spencer
committed
static void sip_registry_destroy(struct sip_registry *reg)
{
/* Really delete */
Olle Johansson
committed
if (option_debug > 2)
ast_log(LOG_DEBUG, "Destroying registry entry for %s@%s\n", reg->username, reg->hostname);
Mark Spencer
committed
if (reg->call) {
/* Clear registry before destroying to ensure
we don't get reentered trying to grab the registry lock */
reg->call->registry = NULL;
Olle Johansson
committed
if (option_debug > 2)
ast_log(LOG_DEBUG, "Destroying active SIP dialog for registry %s@%s\n", reg->username, reg->hostname);
Mark Spencer
committed
sip_destroy(reg->call);
}
if (reg->expire > -1)
ast_sched_del(sched, reg->expire);
if (reg->timeout > -1)
ast_sched_del(sched, reg->timeout);
ast_string_field_free_pools(reg);
Mark Spencer
committed
regobjs--;
free(reg);
}
Olle Johansson
committed
/*! \brief Execute destruction of SIP dialog structure, release memory */
static void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
Olle Johansson
committed
if (sip_debug_test_pvt(p) || option_debug > 2)
ast_verbose("Really destroying SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text);
Kevin P. Fleming
committed
/* Remove link from peer to subscription of MWI */
Olle Johansson
committed
if (p->relatedpeer && p->relatedpeer->mwipvt)
p->relatedpeer->mwipvt = NULL;
Kevin P. Fleming
committed
if (dumphistory)
sip_dump_history(p);
Kevin P. Fleming
committed
if (p->options)
free(p->options);
if (p->stateid > -1)
ast_extension_state_del(p->stateid, NULL);
if (p->initid > -1)
ast_sched_del(sched, p->initid);
if (p->autokillid > -1)
ast_sched_del(sched, p->autokillid);