Newer
Older
return var;
}
static const char *get_name_from_variable(struct ast_variable *var, const char *newpeername)
{
struct ast_variable *tmp;
for (tmp = var; tmp; tmp = tmp->next) {
if (!newpeername && !strcasecmp(tmp->name, "name"))
newpeername = tmp->value;
}
return newpeername;
}
/*! \brief realtime_peer: Get peer from realtime storage
* Checks the "sippeers" realtime family from extconfig.conf
* Checks the "sipregs" realtime family from extconfig.conf if it's configured.
static struct sip_peer *realtime_peer(const char *newpeername, struct sockaddr_in *sin)
struct sip_peer *peer;
struct ast_variable *var = NULL;
struct ast_variable *varregs = NULL;
struct ast_config *peerlist = NULL;
Russell Bryant
committed
char ipaddr[INET_ADDRSTRLEN];
char portstring[6]; /*up to 5 digits plus null terminator*/
char *cat = NULL;
unsigned short portnum;
int realtimeregs = ast_check_realtime("sipregs");
if (newpeername) {
var = ast_load_realtime("sippeers", "name", newpeername, NULL);
if (realtimeregs)
varregs = ast_load_realtime("sipregs", "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));
portnum = ntohs(sin->sin_port);
sprintf(portstring, "%u", portnum);
var = ast_load_realtime("sippeers", "host", ipaddr, "port", portstring, NULL); /* First check for fixed IP hosts */
if (var) {
if (realtimeregs) {
newpeername = get_name_from_variable(var, newpeername);
varregs = ast_load_realtime("sipregs", "name", newpeername, NULL);
}
} else {
if (realtimeregs)
varregs = ast_load_realtime("sipregs", "ipaddr", ipaddr, "port", portstring, NULL); /* Then check for registered hosts */
var = ast_load_realtime("sippeers", "ipaddr", ipaddr, "port", portstring, NULL); /* Then check for registered hosts */
if (varregs) {
newpeername = get_name_from_variable(varregs, newpeername);
var = ast_load_realtime("sippeers", "name", newpeername, NULL);
}
}
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
if(!var) { /*We couldn't match on ipaddress and port, so we need to check if port is insecure*/
peerlist = ast_load_realtime_multientry("sippeers", "host", ipaddr, NULL);
if (peerlist) {
var = get_insecure_variable_from_config(peerlist);
if(var) {
if (realtimeregs) {
newpeername = get_name_from_variable(var, newpeername);
varregs = ast_load_realtime("sipregs", "name", newpeername, NULL);
}
} else { /*var wasn't found in the list of "hosts", so try "ipaddr"*/
peerlist = NULL;
cat = NULL;
peerlist = ast_load_realtime_multientry("sippeers", "ipaddr", ipaddr, NULL);
if(peerlist) {
var = get_insecure_variable_from_config(peerlist);
if(var) {
if (realtimeregs) {
newpeername = get_name_from_variable(var, newpeername);
varregs = ast_load_realtime("sipregs", "name", newpeername, NULL);
}
}
}
}
} else {
if(realtimeregs) {
peerlist = ast_load_realtime_multientry("sipregs", "ipaddr", ipaddr, NULL);
if (peerlist) {
varregs = get_insecure_variable_from_config(peerlist);
if (varregs) {
newpeername = get_name_from_variable(varregs, newpeername);
var = ast_load_realtime("sippeers", "name", newpeername, NULL);
}
}
} else {
peerlist = ast_load_realtime_multientry("sippeers", "ipaddr", ipaddr, NULL);
if (peerlist) {
var = get_insecure_variable_from_config(peerlist);
if (var) {
newpeername = get_name_from_variable(var, newpeername);
varregs = ast_load_realtime("sipregs", "name", newpeername, NULL);
}
}
}
}
}
if (!var) {
if (peerlist)
ast_config_destroy(peerlist);
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")) {
if(peerlist)
ast_config_destroy(peerlist);
else {
ast_variables_destroy(var);
ast_variables_destroy(varregs);
}
} 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);
if(peerlist)
ast_config_destroy(peerlist);
else
ast_variables_destroy(var);
return NULL;
Mark Spencer
committed
/* Peer found in realtime, now build it in memory */
peer = build_peer(newpeername, var, varregs, !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS));
if(peerlist)
ast_config_destroy(peerlist);
else {
ast_variables_destroy(var);
ast_variables_destroy(varregs);
}
return NULL;
Mark Spencer
committed
ast_debug(3,"-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)) {
peer->expire = ast_sched_replace(peer->expire, sched,
global_rtautoclear * 1000, expire_register, (void *) peer);
ASTOBJ_CONTAINER_LINK(&peerl,peer);
} else {
peer->is_realtime = 1;
if (peerlist)
ast_config_destroy(peerlist);
else {
ast_variables_destroy(var);
ast_variables_destroy(varregs);
}
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)
ast_debug(3, "Destroying user object from memory: %s\n", user->name);
ast_variables_destroy(user->chanvars);
user->chanvars = NULL;
if (user->is_realtime)
Mark Spencer
committed
ruserobjs--;
else
suserobjs--;
Tilghman Lesher
committed
ast_free(user);
/*! \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++;
user->is_realtime = 1;
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 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) {
ast_debug(1, "Setting NAT on RTP to %s\n", mode);
ast_rtp_setnat(p->rtp, natflags);
}
if (p->vrtp) {
ast_debug(1, "Setting NAT on VRTP to %s\n", mode);
ast_rtp_setnat(p->vrtp, natflags);
}
if (p->udptl) {
ast_debug(1, "Setting NAT on UDPTL to %s\n", mode);
ast_udptl_setnat(p->udptl, natflags);
}
ast_debug(1, "Setting NAT on TRTP to %s\n", mode);
ast_rtp_setnat(p->trtp, 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;
if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_TEXTSUPPORT) && dialog->trtp) {
ast_rtp_destroy(dialog->trtp);
dialog->trtp = 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;
ast_debug(2,"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_RFC2833);
Olle Johansson
committed
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);
if (dialog->trtp) {
ast_rtp_setdtmf(dialog->trtp, 0);
ast_rtp_setdtmfcompensate(dialog->trtp, 0);
ast_rtp_set_rtptimeout(dialog->trtp, peer->rtptimeout);
ast_rtp_set_rtpholdtimeout(dialog->trtp, peer->rtpholdtimeout);
ast_rtp_set_rtpkeepalive(dialog->trtp, peer->rtpkeepalive);
}
ast_string_field_set(dialog, peername, peer->name);
Olle Johansson
committed
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, mohsuggest, peer->mohsuggest);
ast_string_field_set(dialog, mohinterpret, peer->mohinterpret);
Olle Johansson
committed
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);
}
}
dialog->outboundproxy = obproxy_get(dialog, peer);
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);
if (!ast_strlen_zero(peer->language))
ast_string_field_set(dialog, language, peer->language);
Olle Johansson
committed
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;
dialog->jointnoncodeccapability = dialog->noncodeccapability;
Olle Johansson
committed
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);
ast_string_field_set(dialog, tohost, peername);
/* Get the outbound proxy information */
dialog->outboundproxy = obproxy_get(dialog, NULL);
/* If we have an outbound proxy, don't bother with DNS resolution at all */
if (dialog->outboundproxy)
return 0;
/* Let's see if we can find the host in DNS. First try DNS SRV records,
then hostname lookup */
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);
return -1;
}
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.
* Only called by the scheduler, must return the reference when done.
*/
static int auto_congest(const void *arg)
struct sip_pvt *p = (struct sip_pvt *)arg;
sip_pvt_lock(p);
p->initid = -1; /* event gone, will not be rescheduled */
/* 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 = ast->tech_pvt; /* chan is locked, so the reference cannot go away */
struct varshead *headp;
struct ast_var_t *current;
const char *referer = NULL; /* SIP referrer */
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;
ast_debug(1,"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)
ast_debug(3, "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);
}
ast_debug(1, "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);
p->jointnoncodeccapability = p->noncodeccapability;
/* 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 {
Olle Johansson
committed
int xmitres;
p->t38.jointcapability = p->t38.capability;
ast_debug(2,"Our T38 capability (%d), joint T38 capability (%d)\n", p->t38.capability, p->t38.jointcapability);
Olle Johansson
committed
xmitres = transmit_invite(p, SIP_INVITE, 1, 2);
if (xmitres == XMIT_ERROR)
return -1;
p->invitestate = INV_CALLING;
/* Initialize auto-congest time */
p->initid = ast_sched_replace(p->initid, sched, SIP_TRANS_TIMEOUT,
auto_congest, dialog_ref(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 */
ast_debug(3, "Destroying registry entry for %s@%s\n", reg->username, reg->hostname);
Olle Johansson
committed
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;
ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", reg->username, reg->hostname);
reg->call = sip_destroy(reg->call);
Mark Spencer
committed
}
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--;
Tilghman Lesher
committed
ast_free(reg);
Mark Spencer
committed
}
Olle Johansson
committed
/*! \brief Execute destruction of SIP dialog structure, release memory */
static void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
if (sip_debug_test_pvt(p))
Olle Johansson
committed
ast_verbose("Really destroying SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text);
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);
ast_debug(2, "This call did not properly clean up call limits. Call ID %s\n", p->callid);
Kevin P. Fleming
committed
/* Remove link from peer to subscription of MWI */
Olle Johansson
committed
if (p->relatedpeer && p->relatedpeer->mwipvt)
p->relatedpeer->mwipvt = dialog_unref(p->relatedpeer->mwipvt);
Kevin P. Fleming
committed
if (dumphistory)
sip_dump_history(p);
Kevin P. Fleming
committed
if (p->options)
Tilghman Lesher
committed
ast_free(p->options);
Kevin P. Fleming
committed
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);
ast_rtp_destroy(p->vrtp);
if (p->trtp)
ast_rtp_destroy(p->trtp);
if (p->udptl)
ast_udptl_destroy(p->udptl);
Tilghman Lesher
committed
ast_free(p->refer);
if (p->route) {
free_old_route(p->route);
p->route = NULL;
}
Martin Pycko
committed
if (p->registry) {
Mark Spencer
committed
if (p->registry->call == p)
p->registry->call = NULL;
p->registry = registry_unref(p->registry);
Martin Pycko
committed
}
Kevin P. Fleming
committed
/* Unlink us from the owner if we have one */
if (p->owner) {
ast_debug(1, "Detaching from %s\n", p->owner->name);
p->owner->tech_pvt = NULL;
Mark Spencer
committed
/* Clear history */
Olle Johansson
committed
if (p->history) {
while ( (hist = AST_LIST_REMOVE_HEAD(p->history, list)) ) {
Tilghman Lesher
committed
ast_free(hist);
Tilghman Lesher
committed
ast_free(p->history);
Olle Johansson
committed
p->history = NULL;
Mark Spencer
committed
}
Olle Johansson
committed
/* Lock dialog list before removing ourselves from the list */
if (lockdialoglist)
Olle Johansson
committed
for (prev = NULL, cur = dialoglist; cur; prev = cur, cur = cur->next) {
Olle Johansson
committed
UNLINK(cur, dialoglist, prev);
if (lockdialoglist)
ast_log(LOG_WARNING, "Trying to destroy \"%s\", not found in dialog list?!?! \n", p->callid);
return;
}
Olle Johansson
committed
/* remove all current packets in this dialog */
while((cp = p->packets)) {
p->packets = p->packets->next;
if (cp->retransid > -1)
ast_sched_del(sched, cp->retransid);
dialog_unref(cp->owner);
Tilghman Lesher
committed
ast_free(cp);
if (p->chanvars) {
ast_variables_destroy(p->chanvars);
p->chanvars = NULL;
}
ast_mutex_destroy(&p->pvt_lock);
ast_string_field_free_pools(p);
Tilghman Lesher
committed
ast_free(p);
/*! \brief update_call_counter: Handle call_limit for SIP users
* Setting a call-limit will cause calls above the limit not to be accepted.
*
* Remember that for a type=friend, there's one limit for the user and
* another for the peer, not a combined call limit.
* This will cause unexpected behaviour in subscriptions, since a "friend"
* is *two* devices in Asterisk, not one.
*
* Thought: For realtime, we should probably update storage with inuse counter...
* \return 0 if call is ok (no call limit, below threshold)
* -1 on rejection of call
*
Kevin P. Fleming
committed
static int update_call_counter(struct sip_pvt *fup, int event)
int *inuse = NULL, *call_limit = NULL, *inringing = NULL;
int outgoing = fup->outgoing_call;
Kevin P. Fleming
committed
struct sip_user *u = NULL;
struct sip_peer *p = NULL;
ast_debug(3, "Updating call counter for %s call\n", outgoing ? "outgoing" : "incoming");
/* Test if we need to check call limits, in order to avoid
realtime lookups if we do not need it */
if (!ast_test_flag(&fup->flags[0], SIP_CALL_LIMIT) && !ast_test_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD))
Kevin P. Fleming
committed
ast_copy_string(name, fup->username, sizeof(name));
/* Check the list of users only for incoming calls */
if (global_limitonpeers == FALSE && !outgoing && (u = find_user(name, 1))) {
Olle Johansson
committed
inuse = &u->inUse;
call_limit = &u->call_limit;
inringing = NULL;
} else if ( (p = find_peer(ast_strlen_zero(fup->peername) ? name : fup->peername, NULL, 1) ) ) { /* Try to find peer */
inuse = &p->inUse;
call_limit = &p->call_limit;
inringing = &p->inRinging;
ast_copy_string(name, fup->peername, sizeof(name));
}
if (!p && !u) {
ast_debug(2, "%s is not a local device, no call limit\n", name);
return 0;
/* incoming and outgoing affects the inUse counter */
case DEC_CALL_LIMIT:
/* Decrement inuse count if applicable */
if (inuse && ast_test_flag(&fup->flags[0], SIP_INC_COUNT)) {
ast_atomic_fetchadd_int(inuse, -1);
ast_clear_flag(&fup->flags[0], SIP_INC_COUNT);
} else
*inuse = 0;
/* Decrement ringing count if applicable */
if (inringing && ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
ast_atomic_fetchadd_int(inringing, -1);
ast_clear_flag(&fup->flags[0], SIP_INC_RINGING);
/* Decrement onhold count if applicable */
if (ast_test_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD) && global_notifyhold) {
ast_clear_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD);
sip_peer_hold(fup, FALSE);
if (sipdebug)
ast_debug(2, "Call %s %s '%s' removed from call limit %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *call_limit);
break;
case INC_CALL_RINGING:
case INC_CALL_LIMIT:
/* If call limit is active and we have reached the limit, reject the call */
if (*call_limit > 0 ) {
if (*inuse >= *call_limit) {
ast_log(LOG_ERROR, "Call %s %s '%s' rejected due to usage limit of %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *call_limit);
if (u)
return -1;
}
if (inringing && (event == INC_CALL_RINGING)) {
if (!ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
ast_atomic_fetchadd_int(inringing, +1);
ast_set_flag(&fup->flags[0], SIP_INC_RINGING);
}
}
/* Continue */
ast_atomic_fetchadd_int(inuse, +1);
ast_set_flag(&fup->flags[0], SIP_INC_COUNT);
if (sipdebug) {
ast_debug(2, "Call %s %s '%s' is %d out of %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *inuse, *call_limit);
}
break;
case DEC_CALL_RINGING:
if (inringing && ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
ast_atomic_fetchadd_int(inringing, -1);
ast_clear_flag(&fup->flags[0], SIP_INC_RINGING);
}
break;
default:
ast_log(LOG_ERROR, "update_call_counter(%s, %d) called with no event!\n", name, event);
if (p) {
ast_device_state_changed("SIP/%s", p->name);
} else /* u must be set */
/*! \brief Destroy SIP call structure.
* Make it return NULL so the caller can do things like
* foo = sip_destroy(foo);
* and reduce the chance of bugs due to dangling pointers.
*/
static struct sip_pvt * sip_destroy(struct sip_pvt *p)
ast_debug(3, "Destroying SIP dialog %s\n", p->callid);
__sip_destroy(p, TRUE, TRUE);
return NULL;
/*! \brief Convert SIP hangup causes to Asterisk hangup causes */
Martin Pycko
committed
static int hangup_sip2cause(int cause)
{
/* Possible values taken from causes.h */
Olle Johansson
committed
case 401: /* Unauthorized */
return AST_CAUSE_CALL_REJECTED;
case 403: /* Not found */
return AST_CAUSE_CALL_REJECTED;
case 404: /* Not found */
Olle Johansson
committed
case 405: /* Method not allowed */
return AST_CAUSE_INTERWORKING;
case 407: /* Proxy authentication required */
return AST_CAUSE_CALL_REJECTED;
case 408: /* No reaction */
return AST_CAUSE_NO_USER_RESPONSE;
Olle Johansson
committed
case 409: /* Conflict */
return AST_CAUSE_NORMAL_TEMPORARY_FAILURE;
case 410: /* Gone */
return AST_CAUSE_UNALLOCATED;
case 411: /* Length required */
return AST_CAUSE_INTERWORKING;
case 413: /* Request entity too large */
return AST_CAUSE_INTERWORKING;
case 414: /* Request URI too large */
return AST_CAUSE_INTERWORKING;
case 415: /* Unsupported media type */
return AST_CAUSE_INTERWORKING;
case 420: /* Bad extension */
return AST_CAUSE_NO_ROUTE_DESTINATION;
case 480: /* No answer */
return AST_CAUSE_NO_ANSWER;
Olle Johansson
committed
case 481: /* No answer */
return AST_CAUSE_INTERWORKING;
case 482: /* Loop detected */
return AST_CAUSE_INTERWORKING;
case 483: /* Too many hops */
return AST_CAUSE_NO_ANSWER;
Olle Johansson
committed
case 484: /* Address incomplete */
return AST_CAUSE_INVALID_NUMBER_FORMAT;
Olle Johansson
committed
return AST_CAUSE_UNALLOCATED;
case 486: /* Busy everywhere */
Martin Pycko
committed
return AST_CAUSE_BUSY;
Olle Johansson
committed
case 487: /* Request terminated */
return AST_CAUSE_INTERWORKING;
case 488: /* No codecs approved */
return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
Olle Johansson
committed
case 491: /* Request pending */
return AST_CAUSE_INTERWORKING;
case 493: /* Undecipherable */
return AST_CAUSE_INTERWORKING;
case 500: /* Server internal failure */
return AST_CAUSE_FAILURE;
case 501: /* Call rejected */
return AST_CAUSE_FACILITY_REJECTED;
case 502:
return AST_CAUSE_DESTINATION_OUT_OF_ORDER;
case 503: /* Service unavailable */
return AST_CAUSE_CONGESTION;
Olle Johansson
committed
case 504: /* Gateway timeout */
return AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE;
case 505: /* SIP version not supported */
return AST_CAUSE_INTERWORKING;
case 600: /* Busy everywhere */
return AST_CAUSE_USER_BUSY;
case 603: /* Decline */
return AST_CAUSE_CALL_REJECTED;
case 604: /* Does not exist anywhere */
return AST_CAUSE_UNALLOCATED;
case 606: /* Not acceptable */
return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
Martin Pycko
committed
default:
return AST_CAUSE_NORMAL;
}
/* Never reached */
return 0;
}
/*! \brief Convert Asterisk hangup causes to SIP codes
\verbatim
Possible values from causes.h
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
AST_CAUSE_NOTDEFINED AST_CAUSE_NORMAL AST_CAUSE_BUSY
AST_CAUSE_FAILURE AST_CAUSE_CONGESTION AST_CAUSE_UNALLOCATED
In addition to these, a lot of PRI codes is defined in causes.h
...should we take care of them too ?
Quote RFC 3398
ISUP Cause value SIP response
---------------- ------------
1 unallocated number 404 Not Found
2 no route to network 404 Not found
3 no route to destination 404 Not found
16 normal call clearing --- (*)
17 user busy 486 Busy here
18 no user responding 408 Request Timeout
19 no answer from the user 480 Temporarily unavailable
20 subscriber absent 480 Temporarily unavailable
21 call rejected 403 Forbidden (+)
22 number changed (w/o diagnostic) 410 Gone
22 number changed (w/ diagnostic) 301 Moved Permanently
23 redirection to new destination 410 Gone
26 non-selected user clearing 404 Not Found (=)
27 destination out of order 502 Bad Gateway
28 address incomplete 484 Address incomplete
29 facility rejected 501 Not implemented
31 normal unspecified 480 Temporarily unavailable
static const char *hangup_cause2sip(int cause)
Martin Pycko
committed
{
switch (cause) {
case AST_CAUSE_UNALLOCATED: /* 1 */
case AST_CAUSE_NO_ROUTE_DESTINATION: /* 3 IAX2: Can't find extension in context */
case AST_CAUSE_NO_ROUTE_TRANSIT_NET: /* 2 */
return "404 Not Found";
case AST_CAUSE_CONGESTION: /* 34 */
case AST_CAUSE_SWITCH_CONGESTION: /* 42 */
return "503 Service Unavailable";
case AST_CAUSE_NO_USER_RESPONSE: /* 18 */
return "408 Request Timeout";
case AST_CAUSE_NO_ANSWER: /* 19 */
return "480 Temporarily unavailable";
case AST_CAUSE_CALL_REJECTED: /* 21 */
return "403 Forbidden";
case AST_CAUSE_NUMBER_CHANGED: /* 22 */
return "410 Gone";
case AST_CAUSE_NORMAL_UNSPECIFIED: /* 31 */
return "480 Temporarily unavailable";
case AST_CAUSE_INVALID_NUMBER_FORMAT:
return "484 Address incomplete";
case AST_CAUSE_USER_BUSY:
return "486 Busy here";
case AST_CAUSE_FAILURE:
case AST_CAUSE_FACILITY_REJECTED: /* 29 */
return "501 Not Implemented";
case AST_CAUSE_CHAN_NOT_IMPLEMENTED:
return "503 Service Unavailable";
/* Used in chan_iax2 */
case AST_CAUSE_DESTINATION_OUT_OF_ORDER:
return "502 Bad Gateway";
case AST_CAUSE_BEARERCAPABILITY_NOTAVAIL: /* Can't find codec to connect to host */
return "488 Not Acceptable Here";
case AST_CAUSE_NOTDEFINED:
Martin Pycko
committed
default:
ast_debug(1, "AST hangup cause %d (no match found in SIP)\n", cause);
Martin Pycko
committed
return NULL;
}
Martin Pycko
committed
/* Never reached */