diff options
-rw-r--r-- | channels/chan_sip.c | 366 | ||||
-rw-r--r-- | configs/extconfig.conf.sample | 2 | ||||
-rw-r--r-- | include/asterisk/config.h | 11 | ||||
-rw-r--r-- | main/config.c | 22 |
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; |