diff options
Diffstat (limited to 'channels/chan_iax2.c')
-rw-r--r-- | channels/chan_iax2.c | 787 |
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(®exbuf, 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(®exbuf); @@ -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, ®->addr, NEW_FORCE, 1, defaultsockfd); + reg->callno = find_callno(0, 0, ®->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(®istrations); while ((reg = AST_LIST_REMOVE_HEAD(®istrations, entry))) { @@ -9806,81 +9917,34 @@ static void delete_users(void) } AST_LIST_UNLOCK(®istrations); - 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(®istrations); /* 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(®istrations); - 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); |