Newer
Older
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 */
Russell Bryant
committed
ast_copy_string(req->data + req->len, "\r\n", sizeof(req->data) - req->len);
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 (p->do_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) ?
Olle Johansson
committed
__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 we have an outbound proxy, reset peer address
Only do this once.
*/
if (p->outboundproxy) {
p->sa = p->outboundproxy->ip;
}
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 (p->do_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 == XMIT_CRITICAL), req->method) :
Olle Johansson
committed
__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
}
Olle Johansson
committed
/*! \brief * parses a URI in its components.
*
* \note
* - If scheme is specified, drop it from the top.
Olle Johansson
committed
* - 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.
Olle Johansson
committed
* general form we are expecting is sip[s]:username[:password][;parameter]@host[:port][;...]
*/
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 */
if (scheme) {
int l = strlen(scheme);
if (!strncasecmp(uri, scheme, l))
Olle Johansson
committed
uri += l;
ast_debug(1, "Missing scheme '%s' in '%s'\n", scheme, uri);
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 = "";
Olle Johansson
committed
if ((c = strchr(uri, '@')) == NULL) {
/* domain-only URI, according to the SIP RFC. */
Olle Johansson
committed
dom = uri;
*c++ = '\0';
dom = c;
Olle Johansson
committed
name = uri;
Olle Johansson
committed
/* Remove options in domain and name */
dom = strsep(&dom, ";");
name = strsep(&name, ";");
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 message with Access-URL header, if this is an HTML URL only! */
static int sip_sendhtml(struct ast_channel *chan, int subclass, const char *data, int datalen)
{
struct sip_pvt *p = chan->tech_pvt;
Kevin P. Fleming
committed
ast_string_field_build(p, url, "<%s>;mode=active", data);
if (sip_debug_test_pvt(p))
ast_debug(1, "Send URL %s, state = %d!\n", data, chan->_state);
switch (chan->_state) {
case AST_STATE_RING:
transmit_response(p, "100 Trying", &p->initreq);
break;
case AST_STATE_RINGING:
transmit_response(p, "180 Ringing", &p->initreq);
break;
case AST_STATE_UP:
if (!p->pendinginvite) { /* We are up, and have no outstanding invite */
Russell Bryant
committed
transmit_reinvite_with_sdp(p, FALSE, FALSE);
} else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
}
break;
default:
ast_log(LOG_WARNING, "Don't know how to send URI when state is %d!\n", chan->_state);
}
/*! \brief Deliver SIP call ID for the call */
static char *sip_get_callid(struct ast_channel *chan)
{
struct sip_pvt *p = chan->tech_pvt;
if (!p)
return "";
return ((char *)p->callid);
}
/*! \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.
*/
static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, const char *defaultuser, const char *fullcontact, int expirey)
Russell Bryant
committed
char ipaddr[INET_ADDRSTRLEN];
char *tablename = NULL;
const char *sysname = ast_config_AST_SYSTEM_NAME;
char *syslabel = NULL;
time_t nowtime = time(NULL) + expirey;
const char *fc = fullcontact ? "fullcontact" : NULL;
int realtimeregs = ast_check_realtime("sipregs");
tablename = realtimeregs ? "sipregs" : "sippeers";
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 (sip_cfg.rtsave_sysname)
syslabel = "regserver";
if (fc)
ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
"port", port, "regseconds", regseconds,
"defaultuser", defaultuser, fc, fullcontact, syslabel, sysname, NULL); /* note fc and syslabel _can_ be NULL */
ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
"port", port, "regseconds", regseconds,
"defaultuser", defaultuser, 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",
Russell Bryant
committed
ast_strdup(peer->name), ast_free_ptr, "SIP");
else
ast_context_remove_extension(context, ext, 1, NULL);
}
/*! Destroy mailbox subscriptions */
Russell Bryant
committed
static void destroy_mailbox(struct sip_mailbox *mailbox)
{
if (mailbox->mailbox)
ast_free(mailbox->mailbox);
if (mailbox->context)
ast_free(mailbox->context);
if (mailbox->event_sub)
ast_event_unsubscribe(mailbox->event_sub);
ast_free(mailbox);
}
/*! Destroy all peer-related mailbox subscriptions */
Russell Bryant
committed
static void clear_peer_mailboxes(struct sip_peer *peer)
{
struct sip_mailbox *mailbox;
while ((mailbox = AST_LIST_REMOVE_HEAD(&peer->mailboxes, entry)))
destroy_mailbox(mailbox);
}
/*! \brief Destroy peer object from memory */
Mark Spencer
committed
static void sip_destroy_peer(struct sip_peer *peer)
ast_debug(3, "Destroying SIP peer %s\n", peer->name);
Olle Johansson
committed
if (peer->outboundproxy)
Tilghman Lesher
committed
ast_free(peer->outboundproxy);
peer->outboundproxy = NULL;
/* Delete it, it needs to disappear */
if (peer->call)
peer->call = sip_destroy(peer->call);
Kevin P. Fleming
committed
if (peer->mwipvt) /* We have an active subscription, delete it */
peer->mwipvt = sip_destroy(peer->mwipvt);
Kevin P. Fleming
committed
ast_variables_destroy(peer->chanvars);
peer->chanvars = NULL;
}
/* If the schedule delete fails, that means the schedule is currently
* running, which means we should wait for that thread to complete.
* Otherwise, there's a crashable race condition.
*
* NOTE: once peer is refcounted, this probably is no longer necessary.
*/
while (peer->expire > -1 && ast_sched_del(sched, peer->expire))
usleep(1);
while (peer->pokeexpire > -1 && ast_sched_del(sched, peer->pokeexpire))
usleep(1);
Olle Johansson
committed
register_peer_exten(peer, FALSE);
if (peer->selfdestruct)
Mark Spencer
committed
apeerobjs--;
else if (peer->is_realtime) {
Mark Spencer
committed
rpeerobjs--;
ast_debug(3,"-REALTIME- peer Destroyed. Name: %s. Realtime Peer objects: %d\n", peer->name, rpeerobjs);
Mark Spencer
committed
speerobjs--;
clear_realm_authentication(peer->auth);
peer->auth = NULL;
if (peer->dnsmgr)
ast_dnsmgr_release(peer->dnsmgr);
Russell Bryant
committed
clear_peer_mailboxes(peer);
Tilghman Lesher
committed
ast_free(peer);
/*! \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 (sip_cfg.peer_rtupdate &&
(p->is_realtime || rtcachefriends)) {
Mark Spencer
committed
realtime_update_peer(p->name, &p->addr, p->username, rtcachefriends ? p->fullcontact : NULL, expiry);
static struct ast_variable *get_insecure_variable_from_config(struct ast_config *config)
{
Mark Michelson
committed
struct ast_variable *var = NULL;
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
struct ast_flags flags = {0};
char *cat = NULL;
const char *insecure;
while ((cat = ast_category_browse(config, cat))) {
insecure = ast_variable_retrieve(config, cat, "insecure");
set_insecure_flags(&flags, insecure, -1);
if (ast_test_flag(&flags, SIP_INSECURE_PORT)) {
var = ast_category_root(config, cat);
break;
}
}
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) {
if (realtimeregs)
varregs = ast_load_realtime("sipregs", "name", newpeername, NULL);
var = ast_load_realtime("sippeers", "name", newpeername, "host", "dynamic", NULL);
var = ast_load_realtime("sippeers", "name", newpeername, "host", ast_inet_ntoa(sin->sin_addr), NULL);
if (!var) {
var = ast_load_realtime("sippeers", "name", newpeername, NULL);
/*!\note
* If this one loaded something, then we need to ensure that the host
* field matched. The only reason why we can't have this as a criteria
* is because we only have the IP address and the host field might be
* set as a name (and the reverse PTR might not match).
*/
if (var) {
for (tmp = var; tmp; tmp = tmp->next) {
if (!strcasecmp(var->name, "host")) {
struct in_addr sin2 = { 0, };
struct ast_dnsmgr_entry *dnsmgr = NULL;
if ((ast_dnsmgr_lookup(tmp->value, &sin2, &dnsmgr) < 0) || (memcmp(&sin2, &sin->sin_addr, sizeof(sin2)) != 0)) {
/* No match */
ast_variables_destroy(var);
var = NULL;
}
if (!var && 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);
}
}
if (!var) { /*We couldn't match on ipaddress and port, so we need to check if port is insecure*/
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
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 {
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
\note Avoid using this function in new functions if there's a way to avoid it, i
since it causes a database lookup or a traversal of the in-memory peer list.
*/
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);
Russell Bryant
committed
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 Set the global T38 capabilities on a SIP dialog structure */
Joshua Colp
committed
static void set_t38_capabilities(struct sip_pvt *p)
{
p->t38.capability = global_t38_capability;
if (p->udptl) {
if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_FEC )
p->t38.capability |= T38FAX_UDP_EC_FEC;
else if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_REDUNDANCY )
p->t38.capability |= T38FAX_UDP_EC_REDUNDANCY;
else if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_NONE )
p->t38.capability |= T38FAX_UDP_EC_NONE;
p->t38.capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
}
}
/*! \brief Create address structure from peer reference.
* This function copies data from peer to the dialog, so we don't have to look up the peer
* again from memory or database during the life time of the dialog.
*
* \return -1 on error, 0 on success.
Olle Johansson
committed
static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
dialog->socket = peer->socket;
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)) {
Joshua Colp
committed
ast_copy_flags(&dialog->t38.t38support, &peer->flags[1], SIP_PAGE2_T38SUPPORT);
set_t38_capabilities(dialog);
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);
if (dialog->rtp) { /* Audio */
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;
if (dialog->vrtp) { /* Video */
Olle Johansson
committed
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) { /* Realtime text */
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);
ast_string_field_set(dialog, context, peer->context);
dialog->outboundproxy = obproxy_get(dialog, peer);
dialog->callgroup = peer->callgroup;
dialog->pickupgroup = peer->pickupgroup;
dialog->allowtransfer = peer->allowtransfer;
dialog->jointnoncodeccapability = dialog->noncodeccapability;
dialog->rtptimeout = peer->rtptimeout;
dialog->maxcallbitrate = peer->maxcallbitrate;
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 (!dialog->initreq.headers) {
char *c;
char *tmpcall = ast_strdupa(dialog->callid);
c = strchr(tmpcall, '@');
if (c) {
*c = '\0';
ast_string_field_build(dialog, callid, "%s@%s", tmpcall, 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);
/* Set timer T1 to RTT for this peer (if known by qualify=) */
/* Minimum is settable or default to 100 ms */
Olle Johansson
committed
/* If there is a maxms and lastms from a qualify use that over a manual T1
value. Otherwise, use the peer's T1 value. */
if (peer->maxms && peer->lastms)
Olle Johansson
committed
dialog->timer_t1 = peer->lastms < global_t1min ? global_t1min : peer->lastms;
Olle Johansson
committed
else
dialog->timer_t1 = peer->timer_t1;
/* Set timer B to control transaction timeouts, the peer setting is the default and overrides
the known timer */
if (peer->timer_b)
dialog->timer_b = peer->timer_b;
else
dialog->timer_b = 64 * dialog->timer_t1;
Olle Johansson
committed
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;
Kevin P. Fleming
committed
if (peer->call_limit)
Olle Johansson
committed
ast_set_flag(&dialog->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)
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;
Olle Johansson
committed
dialog->timer_t1 = global_t1; /* Default SIP retransmission timer T1 (RFC 3261) */
dialog->timer_b = global_timer_b; /* Default SIP transaction timer B (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) : (dialog->socket.type & SIP_TRANSPORT_TLS) ? STANDARD_TLS_PORT : STANDARD_SIP_PORT;
if (global_srvlookup) {
char service[MAXHOSTNAMELEN];
int tportno;
int ret;
snprintf(service, sizeof(service), "_sip._%s.%s", get_transport(dialog->socket.type), 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);