summaryrefslogtreecommitdiff
path: root/channels/chan_iax2.c
diff options
context:
space:
mode:
Diffstat (limited to 'channels/chan_iax2.c')
-rw-r--r--channels/chan_iax2.c787
1 files changed, 429 insertions, 358 deletions
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index e9e2e7465..07917f8c4 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -93,6 +93,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/stringfields.h"
#include "asterisk/linkedlists.h"
#include "asterisk/event.h"
+#include "asterisk/astobj2.h"
#include "iax2.h"
#include "iax2-parser.h"
@@ -323,7 +324,6 @@ struct iax2_user {
struct ast_ha *ha;
struct iax2_context *contexts;
struct ast_variable *vars;
- AST_LIST_ENTRY(iax2_user) entry;
};
struct iax2_peer {
@@ -378,7 +378,6 @@ struct iax2_peer {
struct ast_event_sub *mwi_event_sub;
struct ast_ha *ha;
- AST_LIST_ENTRY(iax2_peer) entry;
};
#define IAX2_TRUNK_PREFACE (sizeof(struct iax_frame) + sizeof(struct ast_iax2_meta_hdr) + sizeof(struct ast_iax2_meta_trunk_hdr))
@@ -659,9 +658,24 @@ struct chan_iax2_pvt {
*/
static AST_LIST_HEAD_STATIC(frame_queue, iax_frame);
-static AST_LIST_HEAD_STATIC(users, iax2_user);
+/*!
+ * This module will get much higher performance when doing a lot of
+ * user and peer lookups if the number of buckets is increased from 1.
+ * However, to maintain old behavior for Asterisk 1.4, these are set to
+ * 1 by default. When using multiple buckets, search order through these
+ * containers is considered random, so you will not be able to depend on
+ * the order the entires are specified in iax.conf for matching order. */
+#ifdef LOW_MEMORY
+#define MAX_PEER_BUCKETS 1
+/* #define MAX_PEER_BUCKETS 17 */
+#else
+#define MAX_PEER_BUCKETS 1
+/* #define MAX_PEER_BUCKETS 563 */
+#endif
+static ao2_container *peers;
-static AST_LIST_HEAD_STATIC(peers, iax2_peer);
+#define MAX_USER_BUCKETS MAX_PEER_BUCKETS
+static ao2_container *users;
static AST_LIST_HEAD_STATIC(firmwares, iax_firmware);
@@ -701,7 +715,6 @@ static AST_LIST_HEAD_STATIC(dpcache, iax2_dpcache);
static void reg_source_db(struct iax2_peer *p);
static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in *sin);
-static void destroy_peer(struct iax2_peer *peer);
static int ast_cli_netstats(struct mansession *s, int fd, int limit_fmt);
enum iax2_thread_iostate {
@@ -892,7 +905,6 @@ static struct ast_frame *iax2_read(struct ast_channel *c);
static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly);
static struct iax2_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly);
static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, time_t regtime);
-static void destroy_user(struct iax2_user *user);
static void prune_peers(void);
static void *iax2_dup_variable_datastore(void *);
static void iax2_free_variable_datastore(void *);
@@ -1185,51 +1197,112 @@ static int uncompress_subclass(unsigned char csub)
}
/*!
+ * \note The only member of the peer passed here guaranteed to be set is the name field
+ */
+static int peer_hash_cb(const void *obj, const int flags)
+{
+ const struct iax2_peer *peer = obj;
+
+ return ast_str_hash(peer->name);
+}
+
+/*!
+ * \note The only member of the peer passed here guaranteed to be set is the name field
+ */
+static int peer_cmp_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_peer *peer = obj, *peer2 = arg;
+
+ return !strcasecmp(peer->name, peer2->name) ? CMP_MATCH : 0;
+}
+
+/*!
+ * \note The only member of the user passed here guaranteed to be set is the name field
+ */
+static int user_hash_cb(const void *obj, const int flags)
+{
+ const struct iax2_user *user = obj;
+
+ return ast_str_hash(user->name);
+}
+
+/*!
+ * \note The only member of the user passed here guaranteed to be set is the name field
+ */
+static int user_cmp_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_user *user = obj, *user2 = arg;
+
+ return !strcasecmp(user->name, user2->name) ? CMP_MATCH : 0;
+}
+
+/*!
* \note This funtion calls realtime_peer -> reg_source_db -> iax2_poke_peer -> find_callno,
* so do not call it with a pvt lock held.
*/
static struct iax2_peer *find_peer(const char *name, int realtime)
{
struct iax2_peer *peer = NULL;
+ struct iax2_peer tmp_peer = {
+ .name = name,
+ };
- /* Grab peer from linked list */
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE(&peers, peer, entry) {
- if (!strcasecmp(peer->name, name)) {
- break;
- }
- }
- AST_LIST_UNLOCK(&peers);
+ peer = ao2_find(peers, &tmp_peer, OBJ_POINTER);
/* Now go for realtime if applicable */
if(!peer && realtime)
peer = realtime_peer(name, NULL);
+
+ return peer;
+}
+
+static struct iax2_peer *peer_ref(struct iax2_peer *peer)
+{
+ ao2_ref(peer, +1);
return peer;
}
-static int iax2_getpeername(struct sockaddr_in sin, char *host, int len, int lockpeer)
+static inline struct iax2_peer *peer_unref(struct iax2_peer *peer)
+{
+ ao2_ref(peer, -1);
+ return NULL;
+}
+
+static inline struct iax2_user *user_ref(struct iax2_user *user)
+{
+ ao2_ref(user, +1);
+ return user;
+}
+
+static inline struct iax2_user *user_unref(struct iax2_user *user)
+{
+ ao2_ref(user, -1);
+ return NULL;
+}
+
+static int iax2_getpeername(struct sockaddr_in sin, char *host, int len)
{
struct iax2_peer *peer = NULL;
int res = 0;
+ ao2_iterator i;
- if (lockpeer)
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE(&peers, peer, entry) {
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
if ((peer->addr.sin_addr.s_addr == sin.sin_addr.s_addr) &&
(peer->addr.sin_port == sin.sin_port)) {
ast_copy_string(host, peer->name, len);
+ peer_unref(peer);
res = 1;
break;
}
+ peer_unref(peer);
}
- if (lockpeer)
- AST_LIST_UNLOCK(&peers);
+
if (!peer) {
peer = realtime_peer(NULL, &sin);
if (peer) {
ast_copy_string(host, peer->name, len);
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
+ peer_unref(peer);
res = 1;
}
}
@@ -1237,7 +1310,7 @@ static int iax2_getpeername(struct sockaddr_in sin, char *host, int len, int loc
return res;
}
-static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, int lockpeer, const char *host)
+static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, const char *host)
{
struct chan_iax2_pvt *tmp;
jb_conf jbconf;
@@ -1402,7 +1475,7 @@ static int make_trunk(unsigned short callno, int locked)
*
* \note Calling this function while holding another pvt lock can cause a deadlock.
*/
-static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int lockpeer, int sockfd)
+static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd)
{
int res = 0;
int x;
@@ -1438,7 +1511,7 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc
* this is just checking for a peer that has that IP/port and
* assuming that we have a user of the same name. This isn't always
* correct, but it will be changed if needed after authentication. */
- if (!iax2_getpeername(*sin, host, sizeof(host), lockpeer))
+ if (!iax2_getpeername(*sin, host, sizeof(host)))
snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
now = ast_tvnow();
for (x=1;x<TRUNK_CALL_START;x++) {
@@ -1452,7 +1525,7 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc
ast_log(LOG_WARNING, "No more space\n");
return 0;
}
- iaxs[x] = new_iax(sin, lockpeer, host);
+ iaxs[x] = new_iax(sin, host);
update_max_nontrunk();
if (iaxs[x]) {
if (iaxdebug)
@@ -1940,18 +2013,19 @@ static int send_packet(struct iax_frame *f)
static void iax2_destroy_helper(struct chan_iax2_pvt *pvt)
{
- struct iax2_user *user = NULL;
-
/* Decrement AUTHREQ count if needed */
if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) {
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry) {
- if (!strcmp(user->name, pvt->username)) {
- user->curauthreq--;
- break;
- }
+ struct iax2_user *user;
+ struct iax2_user tmp_user = {
+ .name = pvt->username,
+ };
+
+ user = ao2_find(users, &tmp_user, OBJ_POINTER);
+ if (user) {
+ ast_atomic_fetchadd_int(&user->curauthreq, -1);
+ user_unref(user);
}
- AST_LIST_UNLOCK(&users);
+
ast_clear_flag(pvt, IAX_MAXAUTHREQ);
}
/* No more pings or lagrq's */
@@ -2191,6 +2265,7 @@ static int iax2_prune_realtime(int fd, int argc, char *argv[])
} else {
ast_cli(fd, "SORRY peer %s is not eligible for this operation.\n", argv[3]);
}
+ peer_unref(peer);
} else {
ast_cli(fd, "SORRY peer %s was not found in the cache.\n", argv[3]);
}
@@ -2317,8 +2392,7 @@ static int iax2_show_peer(int fd, int argc, char *argv[])
ast_cli(fd, "%s\n",status);
ast_cli(fd, " Qualify : every %dms when OK, every %dms when UNREACHABLE (sample smoothing %s)\n", peer->pokefreqok, peer->pokefreqnotok, peer->smoothing ? "On" : "Off");
ast_cli(fd,"\n");
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
+ peer_unref(peer);
} else {
ast_cli(fd,"Peer %s not found.\n", argv[3]);
ast_cli(fd,"\n");
@@ -2330,20 +2404,23 @@ static int iax2_show_peer(int fd, int argc, char *argv[])
static char *complete_iax2_show_peer(const char *line, const char *word, int pos, int state)
{
int which = 0;
- struct iax2_peer *p = NULL;
+ struct iax2_peer *peer;
char *res = NULL;
int wordlen = strlen(word);
+ ao2_iterator i;
/* 0 - iax2; 1 - show; 2 - peer; 3 - <peername> */
- if (pos == 3) {
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE(&peers, p, entry) {
- if (!strncasecmp(p->name, word, wordlen) && ++which > state) {
- res = ast_strdup(p->name);
- break;
- }
+ if (pos != 3)
+ return NULL;
+
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
+ if (!strncasecmp(peer->name, word, wordlen) && ++which > state) {
+ res = ast_strdup(peer->name);
+ peer_unref(peer);
+ break;
}
- AST_LIST_UNLOCK(&peers);
+ peer_unref(peer);
}
return res;
@@ -2788,8 +2865,7 @@ static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in
if (strcasecmp(tmp->value, "friend") &&
strcasecmp(tmp->value, "peer")) {
/* Whoops, we weren't supposed to exist! */
- destroy_peer(peer);
- peer = NULL;
+ peer = peer_unref(peer);
break;
}
} else if (!strcasecmp(tmp->name, "regseconds")) {
@@ -2815,9 +2891,7 @@ static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in
peer->expire = ast_sched_replace(peer->expire, sched,
(global_rtautoclear) * 1000, expire_registry, (void *) peer->name);
}
- AST_LIST_LOCK(&peers);
- AST_LIST_INSERT_HEAD(&peers, peer, entry);
- AST_LIST_UNLOCK(&peers);
+ ao2_link(peers, peer_ref(peer));
if (ast_test_flag(peer, IAX_DYNAMIC))
reg_source_db(peer);
} else {
@@ -2872,9 +2946,7 @@ static struct iax2_user *realtime_user(const char *username)
if (ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS)) {
ast_set_flag(user, IAX_RTCACHEFRIENDS);
- AST_LIST_LOCK(&users);
- AST_LIST_INSERT_HEAD(&users, user, entry);
- AST_LIST_UNLOCK(&users);
+ ao2_link(users, user_ref(user));
} else {
ast_set_flag(user, IAX_TEMPONLY);
}
@@ -2916,6 +2988,7 @@ struct create_addr_info {
static int create_addr(const char *peername, struct sockaddr_in *sin, struct create_addr_info *cai)
{
struct iax2_peer *peer;
+ int res = -1;
ast_clear_flag(cai, IAX_SENDANI | IAX_TRUNK);
cai->sockfd = defaultsockfd;
@@ -2937,18 +3010,12 @@ static int create_addr(const char *peername, struct sockaddr_in *sin, struct cre
cai->found = 1;
/* if the peer has no address (current or default), return failure */
- if (!(peer->addr.sin_addr.s_addr || peer->defaddr.sin_addr.s_addr)) {
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
- return -1;
- }
+ if (!(peer->addr.sin_addr.s_addr || peer->defaddr.sin_addr.s_addr))
+ goto return_unref;
/* if the peer is being monitored and is currently unreachable, return failure */
- if (peer->maxms && ((peer->lastms > peer->maxms) || (peer->lastms < 0))) {
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
- return -1;
- }
+ if (peer->maxms && ((peer->lastms > peer->maxms) || (peer->lastms < 0)))
+ goto return_unref;
ast_copy_flags(cai, peer, IAX_SENDANI | IAX_TRUNK | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
cai->maxtime = peer->maxms;
@@ -2976,9 +3043,7 @@ static int create_addr(const char *peername, struct sockaddr_in *sin, struct cre
*key++ = '\0';
if (!key || ast_db_get(family, key, cai->secret, sizeof(cai->secret))) {
ast_log(LOG_WARNING, "Unable to retrieve database password for family/key '%s'!\n", peer->dbsecret);
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
- return -1;
+ goto return_unref;
}
}
@@ -2990,10 +3055,12 @@ static int create_addr(const char *peername, struct sockaddr_in *sin, struct cre
sin->sin_port = peer->defaddr.sin_port;
}
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
+ res = 0;
- return 0;
+return_unref:
+ peer_unref(peer);
+
+ return res;
}
static void __auto_congest(void *nothing)
@@ -3604,18 +3671,20 @@ static int iax2_transfer(struct ast_channel *c, const char *dest)
static int iax2_getpeertrunk(struct sockaddr_in sin)
{
- struct iax2_peer *peer = NULL;
+ struct iax2_peer *peer;
int res = 0;
+ ao2_iterator i;
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE(&peers, peer, entry) {
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
if ((peer->addr.sin_addr.s_addr == sin.sin_addr.s_addr) &&
(peer->addr.sin_port == sin.sin_port)) {
res = ast_test_flag(peer, IAX_TRUNK);
+ peer_unref(peer);
break;
}
+ peer_unref(peer);
}
- AST_LIST_UNLOCK(&peers);
return res;
}
@@ -4374,6 +4443,7 @@ static int iax2_show_users(int fd, int argc, char *argv[])
struct iax2_user *user = NULL;
char auth[90];
char *pstr = "";
+ ao2_iterator i;
switch (argc) {
case 5:
@@ -4390,8 +4460,9 @@ static int iax2_show_users(int fd, int argc, char *argv[])
}
ast_cli(fd, FORMAT, "Username", "Secret", "Authen", "Def.Context", "A/C","Codec Pref");
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry) {
+ i = ao2_iterator_init(users, 0);
+ for (user = ao2_iterator_next(&i); user;
+ user_unref(user), user = ao2_iterator_next(&i)) {
if (havepattern && regexec(&regexbuf, user->name, 0, NULL, 0))
continue;
@@ -4412,9 +4483,7 @@ static int iax2_show_users(int fd, int argc, char *argv[])
ast_cli(fd, FORMAT2, user->name, auth, user->authmethods,
user->contexts ? user->contexts->context : context,
user->ha ? "Yes" : "No", pstr);
-
}
- AST_LIST_UNLOCK(&users);
if (havepattern)
regfree(&regexbuf);
@@ -4432,6 +4501,7 @@ static int __iax2_show_peers(int manager, int fd, struct mansession *s, int argc
int online_peers = 0;
int offline_peers = 0;
int unmonitored_peers = 0;
+ ao2_iterator i;
#define FORMAT2 "%-15.15s %-15.15s %s %-15.15s %-8s %s %-10s%s"
#define FORMAT "%-15.15s %-15.15s %s %-15.15s %-5d%s %s %-10s%s"
@@ -4480,8 +4550,9 @@ static int __iax2_show_peers(int manager, int fd, struct mansession *s, int argc
else
ast_cli(fd, FORMAT2, "Name/Username", "Host", " ", "Mask", "Port", " ", "Status", term);
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE(&peers, peer, entry) {
+ i = ao2_iterator_init(peers, 0);
+ for (peer = ao2_iterator_next(&i); peer;
+ peer_unref(peer), peer = ao2_iterator_next(&i)) {
char nm[20];
char status[20];
char srch[2000];
@@ -4530,7 +4601,6 @@ static int __iax2_show_peers(int manager, int fd, struct mansession *s, int argc
peer->encmethods ? "(E)" : " ", status, term);
total_peers++;
}
- AST_LIST_UNLOCK(&peers);
if (s)
astman_append(s,"%d iax2 peers [%d online, %d offline, %d unmonitored]%s", total_peers, online_peers, offline_peers, unmonitored_peers, term);
@@ -4633,15 +4703,16 @@ static char *complete_iax2_unregister(const char *line, const char *word, int po
/* 0 - iax2; 1 - unregister; 2 - <peername> */
if (pos == 2) {
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE(&peers, p, entry) {
+ ao2_iterator i = ao2_iterator_init(peers, 0);
+ while ((p = ao2_iterator_next(&i))) {
if (!strncasecmp(p->name, word, wordlen) &&
++which > state && p->expire > 0) {
res = ast_strdup(p->name);
+ peer_unref(p);
break;
}
+ peer_unref(p);
}
- AST_LIST_UNLOCK(&peers);
}
return res;
@@ -5036,6 +5107,7 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
int bestscore = 0;
int gotcapability = 0;
struct ast_variable *v = NULL, *tmpvar = NULL;
+ ao2_iterator i;
if (!iaxs[callno])
return res;
@@ -5090,8 +5162,8 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
return res;
}
/* Search the userlist for a compatible entry, and fill in the rest */
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry) {
+ i = ao2_iterator_init(users, 0);
+ while ((user = ao2_iterator_next(&i))) {
if ((ast_strlen_zero(iaxs[callno]->username) || /* No username specified */
!strcmp(iaxs[callno]->username, user->name)) /* Or this username specified */
&& ast_apply_ha(user->ha, sin) /* Access is permitted from this IP */
@@ -5099,6 +5171,8 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
apply_context(user->contexts, iaxs[callno]->context))) { /* Context is permitted */
if (!ast_strlen_zero(iaxs[callno]->username)) {
/* Exact match, stop right now. */
+ if (best)
+ user_unref(best);
best = user;
break;
} else if (ast_strlen_zero(user->secret) && ast_strlen_zero(user->inkeys)) {
@@ -5107,13 +5181,19 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
/* There was host authentication and we passed, bonus! */
if (bestscore < 4) {
bestscore = 4;
+ if (best)
+ user_unref(best);
best = user;
+ continue;
}
} else {
/* No host access, but no secret, either, not bad */
if (bestscore < 3) {
bestscore = 3;
+ if (best)
+ user_unref(best);
best = user;
+ continue;
}
}
} else {
@@ -5121,26 +5201,31 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
/* Authentication, but host access too, eh, it's something.. */
if (bestscore < 2) {
bestscore = 2;
+ if (best)
+ user_unref(best);
best = user;
+ continue;
}
} else {
/* Authentication and no host access... This is our baseline */
if (bestscore < 1) {
bestscore = 1;
+ if (best)
+ user_unref(best);
best = user;
+ continue;
}
}
}
}
+ user_unref(user);
}
- AST_LIST_UNLOCK(&users);
user = best;
if (!user && !ast_strlen_zero(iaxs[callno]->username)) {
user = realtime_user(iaxs[callno]->username);
if (user && !ast_strlen_zero(iaxs[callno]->context) && /* No context specified */
!apply_context(user->contexts, iaxs[callno]->context)) { /* Context is permitted */
- destroy_user(user);
- user = NULL;
+ user = user_unref(user);
}
}
if (user) {
@@ -5219,9 +5304,8 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
ast_string_field_set(iaxs[callno], secret, buf);
} else
ast_string_field_set(iaxs[callno], secret, user->secret);
- if (ast_test_flag(user, IAX_TEMPONLY))
- destroy_user(user);
res = 0;
+ user = user_unref(user);
}
ast_set2_flag(iaxs[callno], iax2_getpeertrunk(*sin), IAX_TRUNK);
return res;
@@ -5267,7 +5351,6 @@ static void merge_encryption(struct chan_iax2_pvt *p, unsigned int enc)
*/
static int authenticate_request(int call_num)
{
- struct iax2_user *user = NULL;
struct iax_ie_data ied;
int res = -1, authreq_restrict = 0;
char challenge[10];
@@ -5277,17 +5360,18 @@ static int authenticate_request(int call_num)
/* If an AUTHREQ restriction is in place, make sure we can send an AUTHREQ back */
if (ast_test_flag(p, IAX_MAXAUTHREQ)) {
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry) {
- if (!strcmp(user->name, p->username)) {
- if (user->curauthreq == user->maxauthreq)
- authreq_restrict = 1;
- else
- user->curauthreq++;
- break;
- }
+ struct iax2_user *user, tmp_user = {
+ .name = p->username,
+ };
+
+ user = ao2_find(users, &tmp_user, OBJ_POINTER);
+ if (user) {
+ if (user->curauthreq == user->maxauthreq)
+ authreq_restrict = 1;
+ else
+ user->curauthreq++;
+ user = user_unref(user);
}
- AST_LIST_UNLOCK(&users);
}
/* If the AUTHREQ limit test failed, send back an error */
@@ -5326,21 +5410,19 @@ static int authenticate_verify(struct chan_iax2_pvt *p, struct iax_ies *ies)
char rsasecret[256] = "";
int res = -1;
int x;
- struct iax2_user *user = NULL;
+ struct iax2_user *user, tmp_user = {
+ .name = p->username,
+ };
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry) {
- if (!strcmp(user->name, p->username))
- break;
- }
+ user = ao2_find(users, &tmp_user, OBJ_POINTER);
if (user) {
if (ast_test_flag(p, IAX_MAXAUTHREQ)) {
- user->curauthreq--;
+ ast_atomic_fetchadd_int(&user->curauthreq, -1);
ast_clear_flag(p, IAX_MAXAUTHREQ);
}
ast_string_field_set(p, host, user->name);
+ user = user_unref(user);
}
- AST_LIST_UNLOCK(&users);
if (!ast_test_flag(&p->state, IAX_STATE_AUTHENTICATED))
return res;
@@ -5402,11 +5484,12 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
char md5secret[256] = "";
char rsasecret[256] = "";
char secret[256] = "";
- struct iax2_peer *p;
+ struct iax2_peer *p = NULL;
struct ast_key *key;
char *keyn;
int x;
int expire = 0;
+ int res = -1;
ast_clear_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED | IAX_STATE_UNCHANGED);
/* iaxs[callno]->peer[0] = '\0'; not necc. any more-- stringfield is pre-inited to null string */
@@ -5433,23 +5516,19 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
if (!p || !iaxs[callno]) {
if (authdebug && !p)
ast_log(LOG_NOTICE, "No registration for peer '%s' (from %s)\n", peer, ast_inet_ntoa(sin->sin_addr));
- return -1;
+ goto return_unref;
}
if (!ast_test_flag(p, IAX_DYNAMIC)) {
if (authdebug)
ast_log(LOG_NOTICE, "Peer '%s' is not dynamic (from %s)\n", peer, ast_inet_ntoa(sin->sin_addr));
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
+ goto return_unref;
}
if (!ast_apply_ha(p->ha, sin)) {
if (authdebug)
ast_log(LOG_NOTICE, "Host %s denied access to register peer '%s'\n", ast_inet_ntoa(sin->sin_addr), p->name);
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
+ goto return_unref;
}
if (!inaddrcmp(&p->addr, sin))
ast_set_flag(&iaxs[callno]->state, IAX_STATE_UNCHANGED);
@@ -5475,16 +5554,12 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
if (!keyn) {
if (authdebug)
ast_log(LOG_NOTICE, "Host %s failed RSA authentication with inkeys '%s'\n", peer, p->inkeys);
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
+ goto return_unref;
}
} else {
if (authdebug)
ast_log(LOG_NOTICE, "Host '%s' trying to do RSA authentication, but we have no inkeys\n", peer);
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
+ goto return_unref;
}
} else if (!ast_strlen_zero(md5secret) && (p->authmethods & IAX_AUTH_MD5) && !ast_strlen_zero(iaxs[callno]->challenge)) {
struct MD5Context md5;
@@ -5508,26 +5583,20 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
} else {
if (authdebug)
ast_log(LOG_NOTICE, "Host %s failed MD5 authentication for '%s' (%s != %s)\n", ast_inet_ntoa(sin->sin_addr), p->name, requeststr, md5secret);
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
+ goto return_unref;
}
} else if (!ast_strlen_zero(secret) && (p->authmethods & IAX_AUTH_PLAINTEXT)) {
/* They've provided a plain text password and we support that */
if (strcmp(secret, p->secret)) {
if (authdebug)
ast_log(LOG_NOTICE, "Host %s did not provide proper plaintext password for '%s'\n", ast_inet_ntoa(sin->sin_addr), p->name);
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
+ goto return_unref;
} else
ast_set_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED);
} else if (!ast_strlen_zero(md5secret) || !ast_strlen_zero(secret)) {
if (authdebug)
ast_log(LOG_NOTICE, "Inappropriate authentication received\n");
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
+ goto return_unref;
}
ast_string_field_set(iaxs[callno], peer, peer);
/* Choose lowest expiry number */
@@ -5536,10 +5605,13 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
+ res = 0;
- return 0;
+return_unref:
+ if (p)
+ peer_unref(p);
+
+ return res;
}
static int authenticate(const char *challenge, const char *secret, const char *keyn, int authmethods, struct iax_ie_data *ied, struct sockaddr_in *sin, ast_aes_encrypt_key *ecx, ast_aes_decrypt_key *dcx)
@@ -5626,8 +5698,8 @@ static int authenticate_reply(struct chan_iax2_pvt *p, struct sockaddr_in *sin,
/* Normal password authentication */
res = authenticate(p->challenge, override, okey, authmethods, &ied, sin, &p->ecx, &p->dcx);
} else {
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE(&peers, peer, entry) {
+ ao2_iterator i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
if ((ast_strlen_zero(p->peer) || !strcmp(p->peer, peer->name))
/* No peer specified at our end, or this is the peer */
&& (ast_strlen_zero(peer->username) || (!strcmp(peer->username, p->username)))
@@ -5636,11 +5708,11 @@ static int authenticate_reply(struct chan_iax2_pvt *p, struct sockaddr_in *sin,
/* No specified host, or this is our host */
) {
res = authenticate(p->challenge, peer->secret, peer->outkey, authmethods, &ied, sin, &p->ecx, &p->dcx);
+ peer_unref(peer);
if (!res)
break;
}
}
- AST_LIST_UNLOCK(&peers);
if (!peer) {
/* We checked our list and didn't find one. It's unlikely, but possible,
that we're trying to authenticate *to* a realtime peer */
@@ -5649,13 +5721,11 @@ static int authenticate_reply(struct chan_iax2_pvt *p, struct sockaddr_in *sin,
if ((peer = realtime_peer(peer_name, NULL))) {
ast_mutex_lock(&iaxsl[callno]);
if (!(p = iaxs[callno])) {
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
+ peer_unref(peer);
return -1;
}
res = authenticate(p->challenge, peer->secret,peer->outkey, authmethods, &ied, sin, &p->ecx, &p->dcx);
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
+ peer_unref(peer);
}
if (!peer) {
ast_mutex_lock(&iaxsl[callno]);
@@ -5997,42 +6067,36 @@ static void prune_peers(void);
static void __expire_registry(void *data)
{
char *name = data;
- struct iax2_peer *p = NULL;
-
- /* Go through and grab this peer... and if it needs to be removed... then do it */
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&peers, p, entry) {
- if (!strcasecmp(p->name, name)) {
- p->expire = -1;
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END
- AST_LIST_UNLOCK(&peers);
+ struct iax2_peer *peer = NULL;
+ struct iax2_peer tmp_peer = {
+ .name = name,
+ };
- /* Peer is already gone for whatever reason */
- if (!p)
+ peer = ao2_find(peers, &tmp_peer, OBJ_POINTER);
+ if (!peer)
return;
- ast_debug(1, "Expiring registration for peer '%s'\n", p->name);
- if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(p, IAX_TEMPONLY|IAX_RTCACHEFRIENDS)))
- realtime_update_peer(p->name, &p->addr, 0);
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", p->name);
+ peer->expire = -1;
+
+ ast_debug(1, "Expiring registration for peer '%s'\n", peer->name);
+ if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(peer, IAX_TEMPONLY|IAX_RTCACHEFRIENDS)))
+ realtime_update_peer(peer->name, &peer->addr, 0);
+ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
/* Reset the address */
- memset(&p->addr, 0, sizeof(p->addr));
+ memset(&peer->addr, 0, sizeof(peer->addr));
/* Reset expiry value */
- p->expiry = min_reg_expire;
- if (!ast_test_flag(p, IAX_TEMPONLY))
- ast_db_del("IAX/Registry", p->name);
- register_peer_exten(p, 0);
- ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */
+ peer->expiry = min_reg_expire;
+ if (!ast_test_flag(peer, IAX_TEMPONLY))
+ ast_db_del("IAX/Registry", peer->name);
+ register_peer_exten(peer, 0);
+ ast_device_state_changed("IAX2/%s", peer->name); /* Activate notification */
if (iax2_regfunk)
- iax2_regfunk(p->name, 0);
+ iax2_regfunk(peer->name, 0);
- if (ast_test_flag(p, IAX_RTAUTOCLEAR)) {
- ast_set_flag(p, IAX_DELME);
- prune_peers();
- }
+ if (ast_test_flag(peer, IAX_RTAUTOCLEAR))
+ ao2_unlink(peers, peer);
+
+ peer_unref(peer);
}
static int expire_registry(void *data)
@@ -6097,6 +6161,7 @@ static int update_registry(struct sockaddr_in *sin, int callno, char *devtype, i
char data[80];
int version;
const char *peer_name;
+ int res = -1;
memset(&ied, 0, sizeof(ied));
@@ -6110,11 +6175,8 @@ static int update_registry(struct sockaddr_in *sin, int callno, char *devtype, i
return -1;
}
ast_mutex_lock(&iaxsl[callno]);
- if (!iaxs[callno]) {
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
- }
+ if (!iaxs[callno])
+ goto return_unref;
if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(p, IAX_TEMPONLY|IAX_RTCACHEFRIENDS))) {
if (sin->sin_addr.s_addr) {
@@ -6153,9 +6215,8 @@ static int update_registry(struct sockaddr_in *sin, int callno, char *devtype, i
/* Make sure our call still exists, an INVAL at the right point may make it go away */
if (!iaxs[callno]) {
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return 0;
+ res = 0;
+ goto return_unref;
}
/* Store socket fd */
@@ -6223,9 +6284,13 @@ static int update_registry(struct sockaddr_in *sin, int callno, char *devtype, i
version = iax_check_version(devtype);
if (version)
iax_ie_append_short(&ied, IAX_IE_FIRMWAREVER, version);
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGACK, 0, ied.buf, ied.pos, -1);
+
+ res = 0;
+
+return_unref:
+ peer_unref(p);
+
+ return res ? res : send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGACK, 0, ied.buf, ied.pos, -1);
}
static int registry_authrequest(int callno)
@@ -6234,6 +6299,7 @@ static int registry_authrequest(int callno)
struct iax2_peer *p;
char challenge[10];
const char *peer_name;
+ int res = -1;
peer_name = ast_strdupa(iaxs[callno]->peer);
@@ -6241,28 +6307,29 @@ static int registry_authrequest(int callno)
ast_mutex_unlock(&iaxsl[callno]);
p = find_peer(peer_name, 1);
ast_mutex_lock(&iaxsl[callno]);
- if (!iaxs[callno]) {
- if (p && ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
+ if (!iaxs[callno])
+ goto return_unref;
+ if (!p) {
+ ast_log(LOG_WARNING, "No such peer '%s'\n", peer_name);
+ goto return_unref;
}
- if (p) {
- memset(&ied, 0, sizeof(ied));
- iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods);
- if (p->authmethods & (IAX_AUTH_RSA | IAX_AUTH_MD5)) {
- /* Build the challenge */
- snprintf(challenge, sizeof(challenge), "%d", (int)ast_random());
- ast_string_field_set(iaxs[callno], challenge, challenge);
- /* snprintf(iaxs[callno]->challenge, sizeof(iaxs[callno]->challenge), "%d", (int)ast_random()); */
- iax_ie_append_str(&ied, IAX_IE_CHALLENGE, iaxs[callno]->challenge);
- }
- iax_ie_append_str(&ied, IAX_IE_USERNAME, peer_name);
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1);;
- }
- ast_log(LOG_WARNING, "No such peer '%s'\n", peer_name);
- return 0;
+
+ memset(&ied, 0, sizeof(ied));
+ iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods);
+ if (p->authmethods & (IAX_AUTH_RSA | IAX_AUTH_MD5)) {
+ /* Build the challenge */
+ snprintf(challenge, sizeof(challenge), "%d", (int)ast_random());
+ ast_string_field_set(iaxs[callno], challenge, challenge);
+ iax_ie_append_str(&ied, IAX_IE_CHALLENGE, iaxs[callno]->challenge);
+ }
+ iax_ie_append_str(&ied, IAX_IE_USERNAME, peer_name);
+
+ res = 0;
+
+return_unref:
+ peer_unref(p);
+
+ return res ? res : send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1);;
}
static int registry_rerequest(struct iax_ies *ies, int callno, struct sockaddr_in *sin)
@@ -6993,7 +7060,7 @@ static int socket_process_meta(int packet_len, struct ast_iax2_meta_hdr *meta, s
/* Stop if we don't have enough data */
if (len > packet_len)
break;
- fr->callno = find_callno(callno & ~IAX_FLAG_FULL, 0, sin, NEW_PREVENT, 1, sockfd);
+ fr->callno = find_callno(callno & ~IAX_FLAG_FULL, 0, sin, NEW_PREVENT, sockfd);
if (!fr->callno)
continue;
@@ -7178,7 +7245,7 @@ static int socket_process(struct iax2_thread *thread)
}
/* This is a video frame, get call number */
- fr->callno = find_callno(ntohs(vh->callno) & ~0x8000, dcallno, &sin, new, 1, fd);
+ fr->callno = find_callno(ntohs(vh->callno) & ~0x8000, dcallno, &sin, new, fd);
minivid = 1;
} else if ((meta->zeros == 0) && !(ntohs(meta->metacmd) & 0x8000))
return socket_process_meta(res, meta, &sin, fd, fr);
@@ -7213,7 +7280,7 @@ static int socket_process(struct iax2_thread *thread)
}
if (!fr->callno)
- fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, 1, fd);
+ fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, fd);
if (fr->callno > 0)
ast_mutex_lock(&iaxsl[fr->callno]);
@@ -8756,7 +8823,7 @@ static int iax2_do_register(struct iax2_registry *reg)
if (!reg->callno) {
ast_debug(1, "Allocate call number\n");
- reg->callno = find_callno(0, 0, &reg->addr, NEW_FORCE, 1, defaultsockfd);
+ reg->callno = find_callno(0, 0, &reg->addr, NEW_FORCE, defaultsockfd);
if (reg->callno < 1) {
ast_log(LOG_WARNING, "Unable to create call for registration\n");
return -1;
@@ -8813,7 +8880,7 @@ static int iax2_provision(struct sockaddr_in *end, int sockfd, char *dest, const
memset(&ied, 0, sizeof(ied));
iax_ie_append_raw(&ied, IAX_IE_PROVISIONING, provdata.buf, provdata.pos);
- callno = find_callno(0, 0, &sin, NEW_FORCE, 1, cai.sockfd);
+ callno = find_callno(0, 0, &sin, NEW_FORCE, cai.sockfd);
if (!callno)
return -1;
@@ -8923,6 +8990,15 @@ static int iax2_poke_noanswer(void *data)
return 0;
}
+static int iax2_poke_peer_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_peer *peer = obj;
+
+ iax2_poke_peer(peer, 0);
+
+ return 0;
+}
+
static int iax2_poke_peer(struct iax2_peer *peer, int heldcall)
{
if (!peer->maxms || !peer->addr.sin_addr.s_addr) {
@@ -8942,7 +9018,7 @@ static int iax2_poke_peer(struct iax2_peer *peer, int heldcall)
}
if (heldcall)
ast_mutex_unlock(&iaxsl[heldcall]);
- peer->callno = find_callno(0, 0, &peer->addr, NEW_FORCE, 0, peer->sockfd);
+ peer->callno = find_callno(0, 0, &peer->addr, NEW_FORCE, peer->sockfd);
if (heldcall)
ast_mutex_lock(&iaxsl[heldcall]);
if (peer->callno < 1) {
@@ -9015,7 +9091,7 @@ static struct ast_channel *iax2_request(const char *type, int format, void *data
if (pds.port)
sin.sin_port = htons(atoi(pds.port));
- callno = find_callno(0, 0, &sin, NEW_FORCE, 1, cai.sockfd);
+ callno = find_callno(0, 0, &sin, NEW_FORCE, cai.sockfd);
if (callno < 1) {
ast_log(LOG_WARNING, "Unable to create call\n");
*cause = AST_CAUSE_CONGESTION;
@@ -9308,7 +9384,31 @@ static int peer_set_srcaddr(struct iax2_peer *peer, const char *srcaddr)
}
}
-
+static void peer_destructor(void *obj)
+{
+ struct iax2_peer *peer = obj;
+
+ ast_free_ha(peer->ha);
+
+ /* Delete it, it needs to disappear */
+ if (peer->expire > -1)
+ ast_sched_del(sched, peer->expire);
+ if (peer->pokeexpire > -1)
+ ast_sched_del(sched, peer->pokeexpire);
+ if (peer->callno > 0) {
+ ast_mutex_lock(&iaxsl[peer->callno]);
+ iax2_destroy(peer->callno);
+ ast_mutex_unlock(&iaxsl[peer->callno]);
+ }
+
+ register_peer_exten(peer, 0);
+
+ if (peer->dnsmgr)
+ ast_dnsmgr_release(peer->dnsmgr);
+
+ ast_string_field_free_pools(peer);
+}
+
/*! \brief Create peer structure based on configuration */
static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly)
{
@@ -9317,38 +9417,30 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
int maskfound = 0;
int found = 0;
int firstpass = 1;
+ struct iax2_peer tmp_peer = {
+ .name = name,
+ };
- AST_LIST_LOCK(&peers);
if (!temponly) {
- AST_LIST_TRAVERSE(&peers, peer, entry) {
- if (!strcmp(peer->name, name)) {
- if (!ast_test_flag(peer, IAX_DELME))
- firstpass = 0;
- break;
- }
- }
- } else
- peer = NULL;
+ peer = ao2_find(peers, &tmp_peer, OBJ_POINTER);
+ if (peer && !ast_test_flag(peer, IAX_DELME))
+ firstpass = 0;
+ }
+
if (peer) {
found++;
if (firstpass) {
oldha = peer->ha;
peer->ha = NULL;
}
- AST_LIST_REMOVE(&peers, peer, entry);
- AST_LIST_UNLOCK(&peers);
- } else {
- AST_LIST_UNLOCK(&peers);
- if ((peer = ast_calloc(1, sizeof(*peer)))) {
- peer->expire = -1;
- peer->pokeexpire = -1;
- peer->sockfd = defaultsockfd;
- if (ast_string_field_init(peer, 32)) {
- ast_free(peer);
- peer = NULL;
- }
- }
+ } else if ((peer = ao2_alloc(sizeof(*peer), peer_destructor))) {
+ peer->expire = -1;
+ peer->pokeexpire = -1;
+ peer->sockfd = defaultsockfd;
+ if (ast_string_field_init(peer, 32))
+ peer = peer_unref(peer);
}
+
if (peer) {
if (firstpass) {
ast_copy_flags(peer, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
@@ -9430,8 +9522,7 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
ast_clear_flag(peer, IAX_DYNAMIC);
if (ast_dnsmgr_lookup(v->value, &peer->addr.sin_addr, &peer->dnsmgr)) {
ast_string_field_free_pools(peer);
- ast_free(peer);
- return NULL;
+ return peer_unref(peer);
}
if (!peer->addr.sin_port)
peer->addr.sin_port = htons(IAX_DEFAULT_PORTNO);
@@ -9441,8 +9532,7 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
} else if (!strcasecmp(v->name, "defaultip")) {
if (ast_get_ip(&peer->defaddr, v->value)) {
ast_string_field_free_pools(peer);
- ast_free(peer);
- return NULL;
+ return peer_unref(peer);
}
} else if (!strcasecmp(v->name, "sourceaddress")) {
peer_set_srcaddr(peer, v->value);
@@ -9562,6 +9652,19 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
return peer;
}
+static void user_destructor(void *obj)
+{
+ struct iax2_user *user = obj;
+
+ ast_free_ha(user->ha);
+ free_context(user->contexts);
+ if(user->vars) {
+ ast_variables_destroy(user->vars);
+ user->vars = NULL;
+ }
+ ast_string_field_free_pools(user);
+}
+
/*! \brief Create in-memory user structure from configuration */
static struct iax2_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly)
{
@@ -9574,18 +9677,15 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
int oldcurauthreq = 0;
char *varname = NULL, *varval = NULL;
struct ast_variable *tmpvar = NULL;
-
- AST_LIST_LOCK(&users);
+ struct iax2_user tmp_user = {
+ .name = name,
+ };
+
if (!temponly) {
- AST_LIST_TRAVERSE(&users, user, entry) {
- if (!strcmp(user->name, name)) {
- if (!ast_test_flag(user, IAX_DELME))
- firstpass = 0;
- break;
- }
- }
- } else
- user = NULL;
+ user = ao2_find(users, &tmp_user, OBJ_POINTER);
+ if (user && !ast_test_flag(user, IAX_DELME))
+ firstpass = 0;
+ }
if (user) {
if (firstpass) {
@@ -9596,12 +9696,9 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
user->contexts = NULL;
}
/* Already in the list, remove it and it will be added back (or FREE'd) */
- AST_LIST_REMOVE(&users, user, entry);
- AST_LIST_UNLOCK(&users);
+ ao2_unlink(users, user);
} else {
- AST_LIST_UNLOCK(&users);
- /* This is going to memset'd to 0 in the next block */
- user = ast_calloc(1, sizeof(*user));
+ user = ao2_alloc(sizeof(*user), user_destructor);
}
if (user) {
@@ -9609,8 +9706,8 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
ast_string_field_free_pools(user);
memset(user, 0, sizeof(struct iax2_user));
if (ast_string_field_init(user, 32)) {
- ast_free(user);
- user = NULL;
+ user = user_unref(user);
+ goto cleanup;
}
user->maxauthreq = maxauthreq;
user->curauthreq = oldcurauthreq;
@@ -9770,6 +9867,7 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
}
ast_clear_flag(user, IAX_DELME);
}
+cleanup:
if (oldha)
ast_free_ha(oldha);
if (oldcon)
@@ -9777,16 +9875,29 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
return user;
}
+static int peer_delme_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_peer *peer = obj;
+
+ ast_set_flag(peer, IAX_DELME);
+
+ return 0;
+}
+
+static int user_delme_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_user *user = obj;
+
+ ast_set_flag(user, IAX_DELME);
+
+ return 0;
+}
+
static void delete_users(void)
{
- struct iax2_user *user;
- struct iax2_peer *peer;
struct iax2_registry *reg;
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry)
- ast_set_flag(user, IAX_DELME);
- AST_LIST_UNLOCK(&users);
+ ao2_callback(users, 0, user_delme_cb, NULL);
AST_LIST_LOCK(&registrations);
while ((reg = AST_LIST_REMOVE_HEAD(&registrations, entry))) {
@@ -9806,81 +9917,34 @@ static void delete_users(void)
}
AST_LIST_UNLOCK(&registrations);
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE(&peers, peer, entry)
- ast_set_flag(peer, IAX_DELME);
- AST_LIST_UNLOCK(&peers);
-}
-
-static void destroy_user(struct iax2_user *user)
-{
- ast_free_ha(user->ha);
- free_context(user->contexts);
- if(user->vars) {
- ast_variables_destroy(user->vars);
- user->vars = NULL;
- }
- ast_string_field_free_pools(user);
- ast_free(user);
+ ao2_callback(peers, 0, peer_delme_cb, NULL);
}
static void prune_users(void)
{
- struct iax2_user *user = NULL;
+ struct iax2_user *user;
+ ao2_iterator i;
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, entry) {
- if (ast_test_flag(user, IAX_DELME)) {
- destroy_user(user);
- AST_LIST_REMOVE_CURRENT(&users, entry);
- }
+ i = ao2_iterator_init(users, 0);
+ while ((user = ao2_iterator_next(&i))) {
+ if (ast_test_flag(user, IAX_DELME))
+ ao2_unlink(users, user);
+ user_unref(user);
}
- AST_LIST_TRAVERSE_SAFE_END
- AST_LIST_UNLOCK(&users);
-
}
-static void destroy_peer(struct iax2_peer *peer)
+/* Prune peers who still are supposed to be deleted */
+static void prune_peers(void)
{
- ast_free_ha(peer->ha);
-
- /* Delete it, it needs to disappear */
- if (peer->expire > -1)
- ast_sched_del(sched, peer->expire);
- if (peer->pokeexpire > -1)
- ast_sched_del(sched, peer->pokeexpire);
- if (peer->callno > 0) {
- ast_mutex_lock(&iaxsl[peer->callno]);
- iax2_destroy(peer->callno);
- ast_mutex_unlock(&iaxsl[peer->callno]);
- }
-
- register_peer_exten(peer, 0);
-
- if (peer->dnsmgr)
- ast_dnsmgr_release(peer->dnsmgr);
-
- if (peer->mwi_event_sub)
- ast_event_unsubscribe(peer->mwi_event_sub);
-
- ast_string_field_free_pools(peer);
-
- ast_free(peer);
-}
-
-static void prune_peers(void){
- /* Prune peers who still are supposed to be deleted */
- struct iax2_peer *peer = NULL;
+ struct iax2_peer *peer;
+ ao2_iterator i;
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&peers, peer, entry) {
- if (ast_test_flag(peer, IAX_DELME)) {
- destroy_peer(peer);
- AST_LIST_REMOVE_CURRENT(&peers, entry);
- }
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
+ if (ast_test_flag(peer, IAX_DELME))
+ ao2_unlink(peers, peer);
+ peer_unref(peer);
}
- AST_LIST_TRAVERSE_SAFE_END
- AST_LIST_UNLOCK(&peers);
}
static void set_timing(void)
@@ -10236,17 +10300,15 @@ static int set_config(char *config_file, int reload)
/* Start with general parameters, then specific parameters, user and peer */
user = build_user(cat, gen, ast_variable_browse(ucfg, cat), 0);
if (user) {
- AST_LIST_LOCK(&users);
- AST_LIST_INSERT_HEAD(&users, user, entry);
- AST_LIST_UNLOCK(&users);
+ ao2_link(users, user);
+ user = NULL;
}
peer = build_peer(cat, gen, ast_variable_browse(ucfg, cat), 0);
if (peer) {
- AST_LIST_LOCK(&peers);
- AST_LIST_INSERT_HEAD(&peers, peer, entry);
- AST_LIST_UNLOCK(&peers);
if (ast_test_flag(peer, IAX_DYNAMIC))
reg_source_db(peer);
+ ao2_link(peers, peer);
+ peer = NULL;
}
}
if (ast_true(registeriax) || (!registeriax && genregisteriax)) {
@@ -10282,19 +10344,17 @@ static int set_config(char *config_file, int reload)
if (!strcasecmp(utype, "user") || !strcasecmp(utype, "friend")) {
user = build_user(cat, ast_variable_browse(cfg, cat), NULL, 0);
if (user) {
- AST_LIST_LOCK(&users);
- AST_LIST_INSERT_HEAD(&users, user, entry);
- AST_LIST_UNLOCK(&users);
+ ao2_link(users, user);
+ user = NULL;
}
}
if (!strcasecmp(utype, "peer") || !strcasecmp(utype, "friend")) {
peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0);
if (peer) {
- AST_LIST_LOCK(&peers);
- AST_LIST_INSERT_HEAD(&peers, peer, entry);
- AST_LIST_UNLOCK(&peers);
if (ast_test_flag(peer, IAX_DYNAMIC))
reg_source_db(peer);
+ ao2_link(peers, peer);
+ peer = NULL;
}
} else if (strcasecmp(utype, "user")) {
ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, config_file);
@@ -10313,7 +10373,6 @@ static int reload_config(void)
{
char *config = "iax.conf";
struct iax2_registry *reg;
- struct iax2_peer *peer;
if (set_config(config, 1) == 1) {
prune_peers();
@@ -10327,11 +10386,9 @@ static int reload_config(void)
AST_LIST_UNLOCK(&registrations);
/* Qualify hosts, too */
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE(&peers, peer, entry)
- iax2_poke_peer(peer, 0);
- AST_LIST_UNLOCK(&peers);
+ ao2_callback(peers, 0, iax2_poke_peer_cb, NULL);
}
+
reload_firmware();
iax_provision_reload(1);
@@ -10384,7 +10441,7 @@ static int cache_get_callno_locked(const char *data)
ast_debug(1, "peer: %s, username: %s, password: %s, context: %s\n",
pds.peer, pds.username, pds.password, pds.context);
- callno = find_callno(0, 0, &sin, NEW_FORCE, 1, cai.sockfd);
+ callno = find_callno(0, 0, &sin, NEW_FORCE, cai.sockfd);
if (callno < 1) {
ast_log(LOG_WARNING, "Unable to create call\n");
return -1;
@@ -10723,8 +10780,7 @@ static int function_iaxpeer(struct ast_channel *chan, const char *cmd, char *dat
}
}
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
+ peer_unref(peer);
return 0;
}
@@ -10838,8 +10894,7 @@ static int iax2_devicestate(void *data)
res = AST_DEVICE_UNKNOWN;
}
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
+ peer_unref(p);
return res;
}
@@ -11143,6 +11198,9 @@ static int __unload_module(void)
for (x = 0; x < IAX_MAX_CALLS; x++)
ast_mutex_destroy(&iaxsl[x]);
+ ao2_ref(peers, -1);
+ ao2_ref(users, -1);
+
return 0;
}
@@ -11153,6 +11211,15 @@ static int unload_module(void)
return __unload_module();
}
+static int peer_set_sock_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_peer *peer = obj;
+
+ if (peer->sockfd < 0)
+ peer->sockfd = defaultsockfd;
+
+ return 0;
+}
/*! \brief Load IAX2 module, load configuraiton ---*/
static int load_module(void)
@@ -11160,8 +11227,16 @@ static int load_module(void)
char *config = "iax.conf";
int x = 0;
struct iax2_registry *reg = NULL;
- struct iax2_peer *peer = NULL;
-
+
+ peers = ao2_container_alloc(MAX_PEER_BUCKETS, peer_hash_cb, peer_cmp_cb);
+ if (!peers)
+ return AST_MODULE_LOAD_FAILURE;
+ users = ao2_container_alloc(MAX_USER_BUCKETS, user_hash_cb, user_cmp_cb);
+ if (!users) {
+ ao2_ref(peers, -1);
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
ast_custom_function_register(&iaxpeer_function);
ast_custom_function_register(&iaxvar_function);
@@ -11245,13 +11320,9 @@ static int load_module(void)
iax2_do_register(reg);
AST_LIST_UNLOCK(&registrations);
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE(&peers, peer, entry) {
- if (peer->sockfd < 0)
- peer->sockfd = defaultsockfd;
- iax2_poke_peer(peer, 0);
- }
- AST_LIST_UNLOCK(&peers);
+ ao2_callback(peers, 0, peer_set_sock_cb, NULL);
+ ao2_callback(peers, 0, iax2_poke_peer_cb, NULL);
+
reload_firmware();
iax_provision_reload(0);