Merged revisions 80362 via svnmerge from
[asterisk/asterisk.git] / channels / chan_iax2.c
index e9e2e74..07917f8 100644 (file)
@@ -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);