Newer
Older
/*! \brief Copy SIP request, parse it */
static void parse_copy(struct sip_request *dst, const struct sip_request *src)
copy_request(dst, src);
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 */
ast_str_append(&req->data, 0, "\r\n");
req->len = ast_str_strlen(req->data);
}
}
/*! \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),
Tilghman Lesher
committed
ntohs(dst->sin_port), req->data->str);
if (p->do_history) {
struct sip_request tmp = { .rlPart1 = 0, };
Olle Johansson
committed
parse_copy(&tmp, req);
Tilghman Lesher
committed
append_history(p, reliable ? "TxRespRel" : "TxResp", "%s / %s - %s", tmp.data->str, get_header(&tmp, "CSeq"),
(tmp.method == SIP_RESPONSE || tmp.method == SIP_UNKNOWN) ? REQ_OFFSET_TO_STR(&tmp, rlPart2) : sip_methods[tmp.method].text);
ast_free(tmp.data);
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);
ast_free(req->data);
Russell Bryant
committed
req->data = NULL;
/*! \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_FORCE_RPORT))
Tilghman Lesher
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->str);
else
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->str);
if (p->do_history) {
struct sip_request tmp = { .rlPart1 = 0, };
Olle Johansson
committed
parse_copy(&tmp, req);
Tilghman Lesher
committed
append_history(p, reliable ? "TxReqRel" : "TxReq", "%s / %s - %s", tmp.data->str, get_header(&tmp, "CSeq"), sip_methods[tmp.method].text);
ast_free(tmp.data);
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);
Russell Bryant
committed
if (req->data) {
ast_free(req->data);
Russell Bryant
committed
req->data = NULL;
}
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
static void enable_digit_detect(struct sip_pvt *p)
{
if (p->dsp) {
return;
}
if (!(p->dsp = ast_dsp_new())) {
return;
}
ast_dsp_set_features(p->dsp, DSP_FEATURE_DIGIT_DETECT);
if (global_relaxdtmf) {
ast_dsp_set_digitmode(p->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
}
}
static void disable_digit_detect(struct sip_pvt *p)
{
if (p->dsp) {
ast_dsp_free(p->dsp);
p->dsp = NULL;
}
}
/*! \brief Set an option on a SIP dialog */
static int sip_setoption(struct ast_channel *chan, int option, void *data, int datalen)
{
int res = -1;
struct sip_pvt *p = chan->tech_pvt;
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
switch (option) {
case AST_OPTION_FORMAT_READ:
res = ast_rtp_instance_set_read_format(p->rtp, *(int *) data);
break;
case AST_OPTION_FORMAT_WRITE:
res = ast_rtp_instance_set_write_format(p->rtp, *(int *) data);
break;
case AST_OPTION_MAKE_COMPATIBLE:
res = ast_rtp_instance_make_compatible(chan, p->rtp, (struct ast_channel *) data);
break;
case AST_OPTION_DIGIT_DETECT:
if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) ||
(ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
char *cp = (char *) data;
ast_debug(1, "%sabling digit detection on %s\n", *cp ? "En" : "Dis", chan->name);
if (*cp) {
enable_digit_detect(p);
} else {
disable_digit_detect(p);
}
res = 0;
}
break;
default:
break;
}
return res;
}
Joshua Colp
committed
/*! \brief Query an option on a SIP dialog */
static int sip_queryoption(struct ast_channel *chan, int option, void *data, int *datalen)
{
int res = -1;
enum ast_t38_state state = T38_STATE_UNAVAILABLE;
struct sip_pvt *p = (struct sip_pvt *) chan->tech_pvt;
char *cp;
Joshua Colp
committed
switch (option) {
case AST_OPTION_T38_STATE:
/* Make sure we got an ast_t38_state enum passed in */
if (*datalen != sizeof(enum ast_t38_state)) {
ast_log(LOG_ERROR, "Invalid datalen for AST_OPTION_T38_STATE option. Expected %d, got %d\n", (int)sizeof(enum ast_t38_state), *datalen);
return -1;
}
sip_pvt_lock(p);
/* Now if T38 support is enabled we need to look and see what the current state is to get what we want to report back */
Kevin P. Fleming
committed
if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) {
Joshua Colp
committed
switch (p->t38.state) {
case T38_LOCAL_REINVITE:
case T38_PEER_REINVITE:
state = T38_STATE_NEGOTIATING;
break;
case T38_ENABLED:
state = T38_STATE_NEGOTIATED;
break;
default:
state = T38_STATE_UNKNOWN;
}
}
sip_pvt_unlock(p);
*((enum ast_t38_state *) data) = state;
res = 0;
break;
case AST_OPTION_DIGIT_DETECT:
cp = (char *) data;
*cp = p->dsp ? 1 : 0;
ast_debug(1, "Reporting digit detection %sabled on %s\n", *cp ? "en" : "dis", chan->name);
Joshua Colp
committed
break;
default:
break;
}
return res;
}
/*! \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][;...]
Olle Johansson
committed
*
*/
static int parse_uri(char *uri, char *scheme,
char **ret_name, char **pass, char **domain, char **port, char **options, char **transport)
{
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 (transport) {
char *t, *type = "";
*transport = "";
if ((t = strstr(uri, "transport="))) {
strsep(&t, "=");
if ((type = strsep(&t, ";"))) {
*transport = type;
}
}
}
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 const char *sip_get_callid(struct ast_channel *chan)
Kevin P. Fleming
committed
return chan->tech_pvt ? ((struct sip_pvt *) chan->tech_pvt)->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;
/* NOT ast_strlen_zero, because a zero-length message is specifically
* allowed by RFC 3428 (See section 10, Examples) */
if (!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, const char *useragent, int expirey, int deprecated_username, int lastms)
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(str_lastms, sizeof(str_lastms), "%d", lastms);
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";
ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
"port", port, "regseconds", regseconds,
deprecated_username ? "username" : "defaultuser", defaultuser,
"useragent", useragent, "lastms", str_lastms,
fc, fullcontact, syslabel, sysname, SENTINEL); /* note fc and syslabel _can_ be NULL */
ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
"port", port, "regseconds", regseconds,
"useragent", useragent, "lastms", str_lastms,
deprecated_username ? "username" : "defaultuser", defaultuser,
syslabel, sysname, SENTINEL); /* 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 (!ast_exists_extension(NULL, context, ext, 1, NULL)) {
ast_add_extension(context, 1, ext, 1, NULL, NULL, "Noop",
ast_strdup(peer->name), ast_free_ptr, "SIP");
} else if (pbx_find_extension(NULL, NULL, &q, context, ext, 1, NULL, "", E_MATCH)) {
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);
}
static void sip_destroy_peer_fn(void *peer)
{
sip_destroy_peer(peer);
}
/*! \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);
if (peer->outboundproxy)
ao2_ref(peer->outboundproxy, -1);
peer->outboundproxy = NULL;
if (peer->call) {
dialog_unlink_all(peer->call, TRUE, TRUE);
peer->call = dialog_unref(peer->call, "peer->call is being unset");
}
Kevin P. Fleming
committed
if (peer->mwipvt) { /* We have an active subscription, delete it */
dialog_unlink_all(peer->mwipvt, TRUE, TRUE);
peer->mwipvt = dialog_unref(peer->mwipvt, "unreffing peer->mwipvt");
}
ast_variables_destroy(peer->chanvars);
peer->chanvars = NULL;
}
Olle Johansson
committed
register_peer_exten(peer, FALSE);
if (peer->selfdestruct)
ast_atomic_fetchadd_int(&apeerobjs, -1);
else if (peer->is_realtime) {
ast_atomic_fetchadd_int(&rpeerobjs, -1);
ast_debug(3, "-REALTIME- peer Destroyed. Name: %s. Realtime Peer objects: %d\n", peer->name, rpeerobjs);
ast_atomic_fetchadd_int(&speerobjs, -1);
clear_realm_authentication(peer->auth);
peer->auth = NULL;
if (peer->dnsmgr)
ast_dnsmgr_release(peer->dnsmgr);
Russell Bryant
committed
clear_peer_mailboxes(peer);
Brett Bryant
committed
Russell Bryant
committed
if (peer->socket.tcptls_session) {
ao2_ref(peer->socket.tcptls_session, -1);
peer->socket.tcptls_session = NULL;
Brett Bryant
committed
}
ast_string_field_free_memory(peer);
/*! \brief Update peer data in database (if used) */
static void update_peer(struct sip_peer *p, int expire)
int rtcachefriends = ast_test_flag(&p->flags[1], SIP_PAGE2_RTCACHEFRIENDS);
if (sip_cfg.peer_rtupdate &&
(p->is_realtime || rtcachefriends)) {
realtime_update_peer(p->name, &p->addr, p->username, rtcachefriends ? p->fullcontact : NULL, p->useragent, expire, p->deprecated_username, p->lastms);
static struct ast_variable *get_insecure_variable_from_config(struct ast_config *cfg)
Mark Michelson
committed
struct ast_variable *var = NULL;
struct ast_flags flags = {0};
char *cat = NULL;
const char *insecure;
while ((cat = ast_category_browse(cfg, cat))) {
insecure = ast_variable_retrieve(cfg, cat, "insecure");
set_insecure_flags(&flags, insecure, -1);
if (ast_test_flag(&flags, SIP_INSECURE_PORT)) {
var = ast_category_root(cfg, 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.
* This returns a pointer to a peer and because we use build_peer, we can rest
* assured that the refcount is bumped.
static struct sip_peer *realtime_peer(const char *newpeername, struct sockaddr_in *sin, int devstate_only)
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, SENTINEL);
var = ast_load_realtime("sippeers", "name", newpeername, "host", "dynamic", SENTINEL);
var = ast_load_realtime("sippeers", "name", newpeername, "host", ast_inet_ntoa(sin->sin_addr), SENTINEL);
var = ast_load_realtime("sippeers", "name", newpeername, SENTINEL);
/*!\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).
*/
for (tmp = var; tmp; tmp = tmp->next) {
if (!strcasecmp(tmp->name, "host")) {
struct hostent *hp;
struct ast_hostent ahp;
if (!(hp = ast_gethostbyname(tmp->value, &ahp)) || (memcmp(&hp->h_addr, &sin->sin_addr, sizeof(hp->h_addr)))) {
/* 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, SENTINEL); /* First check for fixed IP hosts */
if (var) {
if (realtimeregs) {
newpeername = get_name_from_variable(var, newpeername);
varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
}
} else {
if (realtimeregs)
varregs = ast_load_realtime("sipregs", "ipaddr", ipaddr, "port", portstring, SENTINEL); /* Then check for registered hosts */
var = ast_load_realtime("sippeers", "ipaddr", ipaddr, "port", portstring, SENTINEL); /* Then check for registered hosts */
if (varregs) {
newpeername = get_name_from_variable(varregs, newpeername);
var = ast_load_realtime("sippeers", "name", newpeername, SENTINEL);
}
}
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, SENTINEL);
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, SENTINEL);
}
} 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, SENTINEL);
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, SENTINEL);
}
}
}
}
} else {
peerlist = ast_load_realtime_multientry("sipregs", "ipaddr", ipaddr, SENTINEL);
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, SENTINEL);
peerlist = ast_load_realtime_multientry("sippeers", "ipaddr", ipaddr, SENTINEL);
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, SENTINEL);
}
}
}
}
}
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 */
Russell Bryant
committed
peer = build_peer(newpeername, var, varregs, TRUE);
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) && !devstate_only) {
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)) {
AST_SCHED_REPLACE_UNREF(peer->expire, sched, sip_cfg.rtautoclear * 1000, expire_register, peer,
unref_peer(_data, "remove registration ref"),
unref_peer(peer, "remove registration ref"),
ref_peer(peer, "add registration ref"));
ao2_t_link(peers, peer, "link peer into peers table");
if (peer->addr.sin_addr.s_addr) {
ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
}
if (peerlist)
ast_config_destroy(peerlist);
else {
ast_variables_destroy(var);
ast_variables_destroy(varregs);
}
Mark Spencer
committed
Sean Bright
committed
/* Function to assist finding peers by name only */
static int find_by_name(void *obj, void *arg, void *data, int flags)
{
struct sip_peer *search = obj, *match = arg;
Russell Bryant
committed
int *which_objects = data;
Sean Bright
committed
/* Usernames in SIP uri's are case sensitive. Domains are not */
if (strcmp(search->name, match->name)) {
return 0;
}
Russell Bryant
committed
switch (*which_objects) {
case FINDUSERS:
if (!(search->type & SIP_TYPE_USER)) {
return 0;
}
break;
case FINDPEERS:
if (!(search->type & SIP_TYPE_PEER)) {
return 0;
}
break;
case FINDALLDEVICES:
break;
Sean Bright
committed
}
return CMP_MATCH | CMP_STOP;
}
Russell Bryant
committed
/*!
* \brief Locate device by name or ip address
*
* \param which_objects Define which objects should be matched when doing a lookup
* by name. Valid options are FINDUSERS, FINDPEERS, or FINDALLDEVICES.
* Note that this option is not used at all when doing a lookup by IP.
*
* This is used on find matching device on name or ip/port.
Russell Bryant
committed
* If the device was declared as type=peer, we don't match on peer name on incoming INVITEs.
*
* \note Avoid using this function in new functions if there is a way to avoid it,
* since it might cause a database lookup.
*/
static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime, int which_objects, int devstate_only)
struct sip_peer tmp_peer;
Kevin P. Fleming
committed
if (peer) {
ast_copy_string(tmp_peer.name, peer, sizeof(tmp_peer.name));
Russell Bryant
committed
p = ao2_t_callback_data(peers, OBJ_POINTER, find_by_name, &tmp_peer, &which_objects, "ao2_find in peers table");
} else if (sin) { /* search by addr? */
tmp_peer.addr.sin_addr.s_addr = sin->sin_addr.s_addr;
tmp_peer.addr.sin_port = sin->sin_port;
tmp_peer.flags[0].flags = 0;
p = ao2_t_find(peers_by_ip, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_ip table"); /* WAS: p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); */
if (!p) {
ast_set_flag(&tmp_peer.flags[0], SIP_INSECURE_PORT);
p = ao2_t_find(peers_by_ip, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_ip table 2"); /* WAS: p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); */
if (p) {
return p;
}
}
}
if (!p && (realtime || devstate_only)) {
p = realtime_peer(peer, sin, devstate_only);
/*! \brief Set nat mode on the various data sockets */
static void do_setnat(struct sip_pvt *p)
const char *mode;
int natflags;
natflags = ast_test_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP);
mode = natflags ? "On" : "Off";
ast_debug(1, "Setting NAT on RTP to %s\n", mode);
ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_NAT, natflags);
ast_debug(1, "Setting NAT on VRTP to %s\n", mode);
ast_rtp_instance_set_prop(p->vrtp, AST_RTP_PROPERTY_NAT, natflags);
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_instance_set_prop(p->trtp, AST_RTP_PROPERTY_NAT, natflags);
Joshua Colp
committed
/*! \brief Change the T38 state on a SIP dialog */
static void change_t38_state(struct sip_pvt *p, int state)
{
int old = p->t38.state;
struct ast_channel *chan = p->owner;
Kevin P. Fleming
committed
struct ast_control_t38_parameters parameters = { .request_response = 0 };
Joshua Colp
committed
/* Don't bother changing if we are already in the state wanted */
if (old == state)
return;
p->t38.state = state;
ast_debug(2, "T38 state changed to %d on channel %s\n", p->t38.state, chan ? chan->name : "<none>");
/* If no channel was provided we can't send off a control frame */
if (!chan)
return;
/* Given the state requested and old state determine what control frame we want to queue up */
Joshua Colp
committed
if (state == T38_PEER_REINVITE) {
Kevin P. Fleming
committed
parameters = p->t38.their_parms;
parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl);
parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
Joshua Colp
committed
} else if (state == T38_ENABLED) {
Kevin P. Fleming
committed
parameters = p->t38.their_parms;
parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl);
parameters.request_response = AST_T38_NEGOTIATED;
Joshua Colp
committed
} else if (state == T38_DISABLED && old == T38_ENABLED)
Kevin P. Fleming
committed
parameters.request_response = AST_T38_TERMINATED;
Joshua Colp
committed
else if (state == T38_DISABLED && old == T38_LOCAL_REINVITE)
Kevin P. Fleming
committed
parameters.request_response = AST_T38_REFUSED;
Joshua Colp
committed
/* Woot we got a message, create a control frame and send it on! */
Joshua Colp
committed
if (parameters.request_response)
ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, ¶meters, sizeof(parameters));
Dwayne M. Hubbard
committed
Dwayne M. Hubbard
committed
if (ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT) && !p->outgoing_call) {
/* fax detection is enabled and this is an incoming call */
Dwayne M. Hubbard
committed
ast_channel_lock(chan);
if (strcmp(chan->exten, "fax") && state == T38_ENABLED) {
const char *target_context = S_OR(chan->macrocontext, chan->context);
ast_channel_unlock(chan);
if (ast_exists_extension(chan, target_context, "fax", 1, chan->cid.cid_num)) {
/* save the DID/DNIS when we transfer the fax call to a 'fax' extension */
ast_verb(3, "Redirecting '%s' to fax extension\n", chan->name);
pbx_builtin_setvar_helper(chan, "FAXEXTEN", chan->exten);
if (ast_async_goto(chan, target_context, "fax", 1)) {
ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", chan->name, target_context);
}
} else {
ast_log(LOG_NOTICE, "Fax detected but no fax extension.\n");
}
} else {
ast_channel_unlock(chan);
ast_debug(1, "Already in a fax extension (or T38 was not enabled, state: '%d'), not redirecting.\n", state);
}
}
Joshua Colp
committed
}
/*! \brief Set the global T38 capabilities on a SIP dialog structure */
Joshua Colp
committed
static void set_t38_capabilities(struct sip_pvt *p)
{
if (p->udptl) {
Joshua Colp
committed
if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY) {
ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY);
} else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL_FEC) {
ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC);
} else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL) {
ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE);
}
Joshua Colp
committed
}
}
Brett Bryant
committed
static void copy_socket_data(struct sip_socket *to_sock, const struct sip_socket *from_sock)
{
Russell Bryant
committed
if (to_sock->tcptls_session) {
ao2_ref(to_sock->tcptls_session, -1);
to_sock->tcptls_session = NULL;
Brett Bryant
committed
}
Russell Bryant
committed
if (from_sock->tcptls_session) {
ao2_ref(from_sock->tcptls_session, +1);
Brett Bryant
committed
}
*to_sock = *from_sock;
}
/*! \brief Initialize RTP portion of a dialog
* \returns -1 on failure, 0 on success
*/
static int dialog_initialize_rtp(struct sip_pvt *dialog)
{
if (!sip_methods[dialog->method].need_rtp) {
return 0;
}
if (!(dialog->rtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr, NULL))) {
return -1;
}
if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) && (dialog->capability & AST_FORMAT_VIDEO_MASK)) {