summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--channels/chan_sip.c366
-rw-r--r--configs/extconfig.conf.sample2
-rw-r--r--include/asterisk/config.h11
-rw-r--r--main/config.c22
4 files changed, 255 insertions, 146 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 8b9b7cb7e..ff9db3ff7 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -1448,7 +1448,7 @@ static void set_socket_transport(struct sip_socket *socket, int transport);
static void realtime_update_peer(const char *peername, struct ast_sockaddr *addr, const char *username, const char *fullcontact, const char *useragent, int expirey, unsigned short deprecated_username, int lastms);
static void update_peer(struct sip_peer *p, int expire);
static struct ast_variable *get_insecure_variable_from_config(struct ast_config *config);
-static const char *get_name_from_variable(struct ast_variable *var, const char *newpeername);
+static const char *get_name_from_variable(const struct ast_variable *var);
static struct sip_peer *realtime_peer(const char *peername, struct ast_sockaddr *sin, int devstate_only, int which_objects);
static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
@@ -4666,177 +4666,256 @@ static struct ast_variable *get_insecure_variable_from_config(struct ast_config
return var;
}
-static const char *get_name_from_variable(struct ast_variable *var, const char *newpeername)
+static struct ast_variable *get_insecure_variable_from_sippeers(const char *column, const char *value)
{
- struct ast_variable *tmp;
- for (tmp = var; tmp; tmp = tmp->next) {
- if (!newpeername && !strcasecmp(tmp->name, "name"))
- newpeername = tmp->value;
+ struct ast_config *peerlist;
+ struct ast_variable *var = NULL;
+ if ((peerlist = ast_load_realtime_multientry("sippeers", column, value, "insecure LIKE", "%port%", SENTINEL))) {
+ if ((var = get_insecure_variable_from_config(peerlist))) {
+ /* Must clone, because var will get freed along with
+ * peerlist. */
+ var = ast_variables_dup(var);
+ }
+ ast_config_destroy(peerlist);
}
- return newpeername;
+ return var;
}
-/*! \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 ast_sockaddr *addr, int devstate_only, int which_objects)
+/* Yes.. the only column that makes sense to pass is "ipaddr", but for
+ * consistency's sake, we require the column name to be passed. As extra
+ * argument, we take a pointer to var. We already got the info, so we better
+ * return it and save the caller a query. If return value is nonzero, then *var
+ * is nonzero too (and the other way around). */
+static struct ast_variable *get_insecure_variable_from_sipregs(const char *column, const char *value, struct ast_variable **var)
{
- struct sip_peer *peer;
- struct ast_variable *var = NULL;
struct ast_variable *varregs = NULL;
- struct ast_variable *tmp;
- struct ast_config *peerlist = NULL;
- char ipaddr[INET6_ADDRSTRLEN];
- char portstring[6]; /*up to 5 digits plus null terminator*/
- int realtimeregs = ast_check_realtime("sipregs");
+ struct ast_config *regs, *peers;
+ char *regscat;
+ const char *regname;
- /* First check on peer name */
- if (newpeername) {
- if (realtimeregs)
- varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
-
- var = ast_load_realtime("sippeers", "name", newpeername, "host", "dynamic", SENTINEL);
- if (!var && addr) {
- var = ast_load_realtime("sippeers", "name", newpeername, "host", ast_sockaddr_stringify_addr(addr), SENTINEL);
- }
- if (!var) {
- 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).
- */
- if (var && addr) {
- for (tmp = var; tmp; tmp = tmp->next) {
- if (!strcasecmp(tmp->name, "host")) {
- struct ast_sockaddr *addrs = NULL;
-
- if (ast_sockaddr_resolve(&addrs,
- tmp->value,
- PARSE_PORT_FORBID,
- get_address_family_filter(&bindaddr)) <= 0 ||
- ast_sockaddr_cmp(&addrs[0], addr)) {
- /* No match */
- ast_variables_destroy(var);
- var = NULL;
+ if (!(regs = ast_load_realtime_multientry("sipregs", column, value, SENTINEL))) {
+ return NULL;
+ }
+
+ /* Load *all* peers that are probably insecure=port */
+ if (!(peers = ast_load_realtime_multientry("sippeers", "insecure LIKE", "%port%", SENTINEL))) {
+ ast_config_destroy(regs);
+ return NULL;
+ }
+
+ /* Loop over the sipregs that match IP address and attempt to find an
+ * insecure=port match to it in sippeers. */
+ regscat = NULL;
+ while ((regscat = ast_category_browse(regs, regscat)) && (regname = ast_variable_retrieve(regs, regscat, "name"))) {
+ char *peerscat;
+ const char *peername;
+
+ peerscat = NULL;
+ while ((peerscat = ast_category_browse(peers, peerscat)) && (peername = ast_variable_retrieve(peers, peerscat, "name"))) {
+ if (!strcasecmp(regname, peername)) {
+ /* Ensure that it really is insecure=port and
+ * not something else. */
+ const char *insecure = ast_variable_retrieve(peers, peerscat, "insecure");
+ struct ast_flags flags = {0};
+ set_insecure_flags(&flags, insecure, -1);
+ if (ast_test_flag(&flags, SIP_INSECURE_PORT)) {
+ /* ENOMEM checks till the bitter end. */
+ if ((varregs = ast_variables_dup(ast_category_root(regs, regscat)))) {
+ if (!(*var = ast_variables_dup(ast_category_root(peers, peerscat)))) {
+ ast_variables_destroy(varregs);
+ varregs = NULL;
}
- ast_free(addrs);
- break;
}
+ goto done;
}
}
}
}
- if (!var && addr) { /* Then check on IP address for dynamic peers */
- ast_copy_string(ipaddr, ast_sockaddr_stringify_addr(addr), sizeof(ipaddr));
- ast_copy_string(portstring, ast_sockaddr_stringify_port(addr), sizeof(portstring));
- 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 */
- else
- 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;
- 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 {
- if (realtimeregs) {
- 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);
- }
- }
- } else {
- 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);
- }
+done:
+ ast_config_destroy(regs);
+ ast_config_destroy(peers);
+ return varregs;
+}
+
+static const char *get_name_from_variable(const struct ast_variable *var)
+{
+ const struct ast_variable *tmp;
+ for (tmp = var; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, "name")) {
+ return tmp->value;
+ }
+ }
+ return NULL;
+}
+
+/* If varregs is NULL, we don't use sipregs.
+ * Using empty if-bodies instead of goto's while avoiding unnecessary indents */
+static int realtime_peer_by_name(const char *const *name, struct ast_sockaddr *addr, const char *ipaddr, struct ast_variable **var, struct ast_variable **varregs)
+{
+ /* Peer by name and host=dynamic */
+ if ((*var = ast_load_realtime("sippeers", "name", *name, "host", "dynamic", SENTINEL))) {
+ ;
+ /* Peer by name and host=IP */
+ } else if (addr && !(*var = ast_load_realtime("sippeers", "name", *name, "host", ipaddr, SENTINEL))) {
+ ;
+ /* Peer by name and host=HOSTNAME */
+ } else if ((*var = ast_load_realtime("sippeers", "name", *name, 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).
+ */
+ if (addr) {
+ struct ast_variable *tmp;
+ for (tmp = *var; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, "host")) {
+ struct ast_sockaddr *addrs = NULL;
+
+ if (ast_sockaddr_resolve(&addrs,
+ tmp->value,
+ PARSE_PORT_FORBID,
+ get_address_family_filter(&bindaddr)) <= 0 ||
+ ast_sockaddr_cmp(&addrs[0], addr)) {
+ /* No match */
+ ast_variables_destroy(*var);
+ *var = NULL;
}
+ ast_free(addrs);
+ break;
}
}
}
}
- if (!var) {
- if (peerlist)
- ast_config_destroy(peerlist);
- return NULL;
+ /* Did we find anything? */
+ if (*var) {
+ if (varregs) {
+ *varregs = ast_load_realtime("sipregs", "name", *name, SENTINEL);
+ }
+ return 1;
}
+ return 0;
+}
- for (tmp = var; tmp; tmp = tmp->next) {
- if (!strcasecmp(tmp->name, "type") && (!strcasecmp(tmp->value, "peer") && which_objects == FINDUSERS)) {
- if (peerlist) {
- ast_config_destroy(peerlist);
- } else {
- ast_variables_destroy(var);
- ast_variables_destroy(varregs);
- }
- return NULL;
- } else if (!newpeername && !strcasecmp(tmp->name, "name")) {
- newpeername = tmp->value;
+/* Another little helper function for backwards compatibility: this
+ * checks/fetches the sippeer that belongs to the sipreg. If none is
+ * found, we free the sipreg and return false. This way we can do the
+ * check inside the if-condition below. In the old code, not finding
+ * the sippeer also had it continue look for another match, so we do
+ * the same. */
+static struct ast_variable *realtime_peer_get_sippeer_helper(const char **name, struct ast_variable **varregs) {
+ struct ast_variable *var;
+ const char *old_name = *name;
+ *name = get_name_from_variable(*varregs);
+ if (!(var = ast_load_realtime("sippeers", "name", *name, SENTINEL))) {
+ ast_variables_destroy(*varregs);
+ *varregs = NULL;
+ *name = old_name;
+ }
+ return var;
+}
+
+/* If varregs is NULL, we don't use sipregs. If we return true, then *name is
+ * set. Using empty if-bodies instead of goto's while avoiding unnecessary
+ * indents. */
+static int realtime_peer_by_addr(const char **name, struct ast_sockaddr *addr, const char *ipaddr, struct ast_variable **var, struct ast_variable **varregs)
+{
+ char portstring[6]; /* up to 5 digits plus null terminator */
+ ast_copy_string(portstring, ast_sockaddr_stringify_port(addr), sizeof(portstring));
+
+ /* We're not finding this peer by this name anymore. Reset it. */
+ *name = NULL;
+
+ /* First check for fixed IP hosts */
+ if ((*var = ast_load_realtime("sippeers", "host", ipaddr, "port", portstring, SENTINEL))) {
+ ;
+ /* Check for registered hosts (in sipregs) */
+ } else if (varregs && (*varregs = ast_load_realtime("sipregs", "ipaddr", ipaddr, "port", portstring, SENTINEL)) &&
+ (*var = realtime_peer_get_sippeer_helper(name, varregs))) {
+ ;
+ /* Check for registered hosts (in sippeers) */
+ } else if (!varregs && (*var = ast_load_realtime("sippeers", "ipaddr", ipaddr, "port", portstring, SENTINEL))) {
+ ;
+ /* We couldn't match on ipaddress and port, so we need to check if port is insecure */
+ } else if ((*var = get_insecure_variable_from_sippeers("host", ipaddr))) {
+ ;
+ /* Same as above, but try the IP address field (in sipregs)
+ * Observe that it fetches the name/var at the same time, without the
+ * realtime_peer_get_sippeer_helper. Also note that it is quite inefficient.
+ * Avoid sipregs if possible. */
+ } else if (varregs && (*varregs = get_insecure_variable_from_sipregs("ipaddr", ipaddr, var))) {
+ ;
+ /* Same as above, but try the IP address field (in sippeers) */
+ } else if (!varregs && (*var = get_insecure_variable_from_sippeers("ipaddr", ipaddr))) {
+ ;
+ }
+
+ /* Did we find anything? */
+ if (*var) {
+ /* Make sure name is populated. */
+ if (!*name) {
+ *name = get_name_from_variable(*var);
+ }
+ /* Make sure varregs is populated if var is. Ensuring
+ * that var is set when varregs is, is taken care of by
+ * realtime_peer_get_sippeer_helper(). */
+ if (varregs && !*varregs) {
+ *varregs = ast_load_realtime("sipregs", "name", *name, SENTINEL);
}
+ return 1;
}
+ return 0;
+}
- if (!newpeername) { /* Did not find peer in realtime */
+/*! \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.
+ *
+ * \note This is never called with both newpeername and addr at the same time.
+ * If you do, be prepared to get a peer with a different name than newpeername.
+ */
+static struct sip_peer *realtime_peer(const char *newpeername, struct ast_sockaddr *addr, int devstate_only, int which_objects)
+{
+ struct sip_peer *peer = NULL;
+ struct ast_variable *var = NULL;
+ struct ast_variable *varregs = NULL;
+ char ipaddr[INET6_ADDRSTRLEN];
+ int realtimeregs = ast_check_realtime("sipregs");
+
+ if (addr) {
+ ast_copy_string(ipaddr, ast_sockaddr_stringify_addr(addr), sizeof(ipaddr));
+ } else {
+ ipaddr[0] = '\0';
+ }
+
+ if (newpeername && realtime_peer_by_name(&newpeername, addr, ipaddr, &var, realtimeregs ? &varregs : NULL)) {
+ ;
+ } else if (addr && realtime_peer_by_addr(&newpeername, addr, ipaddr, &var, realtimeregs ? &varregs : NULL)) {
+ ;
+ } else {
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;
}
+ /* If we're looking for users, don't return peers (although this check
+ * should probably be done in realtime_peer_by_* instead...) */
+ if (which_objects == FINDUSERS) {
+ struct ast_variable *tmp;
+ for (tmp = var; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, "type") && (!strcasecmp(tmp->value, "peer"))) {
+ goto cleanup;
+ }
+ }
+ }
/* Peer found in realtime, now build it in memory */
peer = build_peer(newpeername, var, varregs, TRUE, devstate_only);
if (!peer) {
- if(peerlist)
- ast_config_destroy(peerlist);
- else {
- ast_variables_destroy(var);
- ast_variables_destroy(varregs);
- }
- return NULL;
+ goto cleanup;
}
ast_debug(3, "-REALTIME- loading peer from database to memory. Name: %s. Peer objects: %d\n", peer->name, rpeerobjs);
@@ -4856,13 +4935,10 @@ static struct sip_peer *realtime_peer(const char *newpeername, struct ast_sockad
}
}
peer->is_realtime = 1;
- if (peerlist)
- ast_config_destroy(peerlist);
- else {
- ast_variables_destroy(var);
- ast_variables_destroy(varregs);
- }
+cleanup:
+ ast_variables_destroy(var);
+ ast_variables_destroy(varregs);
return peer;
}
diff --git a/configs/extconfig.conf.sample b/configs/extconfig.conf.sample
index e08674e9d..70d76ac08 100644
--- a/configs/extconfig.conf.sample
+++ b/configs/extconfig.conf.sample
@@ -64,7 +64,7 @@
;iaxusers => odbc,asterisk
;iaxpeers => odbc,asterisk
;sippeers => odbc,asterisk
-;sipregs => odbc,asterisk
+;sipregs => odbc,asterisk ; (avoid sipregs if possible, e.g. by using a view)
;voicemail => odbc,asterisk
;extensions => odbc,asterisk
;meetme => mysql,general
diff --git a/include/asterisk/config.h b/include/asterisk/config.h
index 932404d14..86c2bb5dd 100644
--- a/include/asterisk/config.h
+++ b/include/asterisk/config.h
@@ -450,6 +450,17 @@ int ast_check_realtime(const char *family);
int ast_realtime_enabled(void);
/*!
+ * \brief Duplicate variable list
+ * \param var the linked list of variables to clone
+ * \return A duplicated list which you'll need to free with
+ * ast_variables_destroy or NULL when out of memory.
+ *
+ * \note Do not depend on this to copy more than just name, value and filename
+ * (the arguments to ast_variables_new).
+ */
+struct ast_variable *ast_variables_dup(struct ast_variable *var);
+
+/*!
* \brief Free variable list
* \param var the linked list of variables to free
*
diff --git a/main/config.c b/main/config.c
index 23d98da8a..498ae993d 100644
--- a/main/config.c
+++ b/main/config.c
@@ -525,6 +525,28 @@ static void ast_variable_destroy(struct ast_variable *doomed)
ast_free(doomed);
}
+struct ast_variable *ast_variables_dup(struct ast_variable *var)
+{
+ struct ast_variable *cloned;
+ struct ast_variable *tmp;
+
+ if (!(cloned = ast_variable_new(var->name, var->value, var->file))) {
+ return NULL;
+ }
+
+ tmp = cloned;
+
+ while ((var = var->next)) {
+ if (!(tmp->next = ast_variable_new(var->name, var->value, var->file))) {
+ ast_variables_destroy(cloned);
+ return NULL;
+ }
+ tmp = tmp->next;
+ }
+
+ return cloned;
+}
+
void ast_variables_destroy(struct ast_variable *v)
{
struct ast_variable *vn;