diff options
Diffstat (limited to 'channels/chan_iax2.c')
-rw-r--r-- | channels/chan_iax2.c | 389 |
1 files changed, 255 insertions, 134 deletions
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 8d031f7ea..454c4ee86 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -89,6 +89,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/dnsmgr.h" #include "asterisk/devicestate.h" #include "asterisk/netsock.h" +#include "asterisk/dlinkedlists.h" +#include "asterisk/astobj2.h" #include "iax2.h" #include "iax2-parser.h" @@ -611,6 +613,8 @@ struct chan_iax2_pvt { int frames_dropped; /*! received frame count: (just for stats) */ int frames_received; + + AST_DLLIST_ENTRY(chan_iax2_pvt) entry; }; static struct ast_iax2_queue { @@ -736,6 +740,16 @@ static struct chan_iax2_pvt *iaxs[IAX_MAX_CALLS]; static ast_mutex_t iaxsl[IAX_MAX_CALLS]; static struct timeval lastused[IAX_MAX_CALLS]; +/*! + * \brief Another container of iax2_pvt structures + * + * Active IAX2 pvt structs are also stored in this container, if they are a part + * of an active call where we know the remote side's call number. The reason + * for this is that incoming media frames do not contain our call number. So, + * instead of having to iterate the entire iaxs array, we use this container to + * look up calls where the remote side is using a given call number. + */ +static struct ao2_container *iax_peercallno_pvts; static int send_command(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int); static int send_command_locked(unsigned short callno, char, int, unsigned int, const unsigned char *, int, int); @@ -913,17 +927,188 @@ static int iax2_getpeername(struct sockaddr_in sin, char *host, int len, int loc return res; } +static void update_max_trunk(void) +{ + int max = TRUNK_CALL_START; + int x; + /* XXX Prolly don't need locks here XXX */ + for (x=TRUNK_CALL_START;x<IAX_MAX_CALLS - 1; x++) { + if (iaxs[x]) + max = x + 1; + } + maxtrunkcall = max; + if (option_debug && iaxdebug) + ast_log(LOG_DEBUG, "New max trunk callno is %d\n", max); +} + +static void iax2_destroy_helper(struct chan_iax2_pvt *pvt) +{ + if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) { + struct iax2_user *user; + + ast_mutex_lock(&userl.lock); + user = userl.users; + while (user) { + if (!strcmp(user->name, pvt->username)) { + user->curauthreq--; + break; + } + user = user->next; + } + ast_mutex_unlock(&userl.lock); + } + + /* No more pings or lagrq's */ + if (pvt->pingid > -1) + ast_sched_del(sched, pvt->pingid); + if (pvt->lagid > -1) + ast_sched_del(sched, pvt->lagid); + if (pvt->autoid > -1) + ast_sched_del(sched, pvt->autoid); + if (pvt->authid > -1) + ast_sched_del(sched, pvt->authid); + if (pvt->initid > -1) + ast_sched_del(sched, pvt->initid); +#ifdef NEWJB + if (pvt->jbid > -1) + ast_sched_del(sched, pvt->jbid); + pvt->jbid = -1; +#endif + pvt->pingid = -1; + pvt->lagid = -1; + pvt->autoid = -1; + pvt->authid = -1; + pvt->initid = -1; +} + +static void store_by_peercallno(struct chan_iax2_pvt *pvt) +{ + if (!pvt->peercallno) { + ast_log(LOG_ERROR, "This should not be called without a peer call number.\n"); + return; + } + + ao2_link(iax_peercallno_pvts, pvt); +} + +static void remove_by_peercallno(struct chan_iax2_pvt *pvt) +{ + if (!pvt->peercallno) { + ast_log(LOG_ERROR, "This should not be called without a peer call number.\n"); + return; + } + + ao2_unlink(iax_peercallno_pvts, pvt); +} + + + +static void iax2_destroy(int callno) +{ + struct chan_iax2_pvt *pvt; + struct ast_channel *owner; + +retry: + ast_mutex_lock(&iaxsl[callno]); + pvt = iaxs[callno]; + gettimeofday(&lastused[callno], NULL); + + if (pvt) + owner = pvt->owner; + else + owner = NULL; + if (owner) { + if (ast_mutex_trylock(&owner->lock)) { + ast_log(LOG_NOTICE, "Avoiding IAX destroy deadlock\n"); + ast_mutex_unlock(&iaxsl[callno]); + usleep(1); + goto retry; + } + } + if (!owner) + iaxs[callno] = NULL; + if (pvt) { + if (owner) { + /* If there's an owner, prod it to give up */ + owner->_softhangup |= AST_SOFTHANGUP_DEV; + ast_queue_hangup(owner); + } else { + pvt->owner = NULL; + } + + if (pvt->peercallno) { + remove_by_peercallno(pvt); + } + + if (!owner) { + ao2_ref(pvt, -1); + pvt = NULL; + } + } + + if (owner) { + ast_mutex_unlock(&owner->lock); + } + + ast_mutex_unlock(&iaxsl[callno]); + + if (callno & 0x4000) { + update_max_trunk(); + } +} + +static void iax2_frame_free(struct iax_frame *fr) +{ + if (fr->retrans > -1) + ast_sched_del(sched, fr->retrans); + iax_frame_free(fr); +} + +static void pvt_destructor(void *obj) +{ + struct chan_iax2_pvt *pvt = obj; + struct iax_frame *cur; + + iax2_destroy_helper(pvt); + + if (pvt->bridgetrans) + ast_translator_free_path(pvt->bridgetrans); + pvt->bridgetrans = NULL; + + /* Already gone */ + ast_set_flag(pvt, IAX_ALREADYGONE); + + for (cur = iaxq.head; cur ; cur = cur->next) { + /* Cancel any pending transmissions */ + if (cur->callno == pvt->callno) + cur->retries = -1; + } + if (pvt->reg) { + pvt->reg->callno = 0; + } + if (!pvt->owner) { + if (pvt->vars) { + ast_variables_destroy(pvt->vars); + pvt->vars = NULL; + } +#ifdef NEWJB + { + jb_frame frame; + while (jb_getall(pvt->jb,&frame) == JB_OK) + iax2_frame_free(frame.data); + jb_destroy(pvt->jb); + } +#endif + } +} + static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, int lockpeer, const char *host) { struct chan_iax2_pvt *tmp; - tmp = malloc(sizeof(struct chan_iax2_pvt)); + + tmp = ao2_alloc(sizeof(*tmp), pvt_destructor); if (tmp) { - memset(tmp, 0, sizeof(struct chan_iax2_pvt)); tmp->prefs = prefs; - tmp->callno = 0; - tmp->peercallno = 0; - tmp->transfercallno = 0; - tmp->bridgecallno = 0; tmp->pingid = -1; tmp->lagid = -1; tmp->autoid = -1; @@ -987,20 +1172,6 @@ static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short return 0; } -static void update_max_trunk(void) -{ - int max = TRUNK_CALL_START; - int x; - /* XXX Prolly don't need locks here XXX */ - for (x=TRUNK_CALL_START;x<IAX_MAX_CALLS - 1; x++) { - if (iaxs[x]) - max = x + 1; - } - maxtrunkcall = max; - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "New max trunk callno is %d\n", max); -} - static void update_max_nontrunk(void) { int max = 1; @@ -1070,6 +1241,25 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc char iabuf[INET_ADDRSTRLEN]; char host[80]; if (new <= NEW_ALLOW) { + if (callno) { + struct chan_iax2_pvt *pvt; + struct chan_iax2_pvt tmp_pvt = { + .callno = dcallno, + .peercallno = callno, + /* hack!! */ + .frames_received = full_frame, + }; + + memcpy(&tmp_pvt.addr, sin, sizeof(tmp_pvt.addr)); + + if ((pvt = ao2_find(iax_peercallno_pvts, &tmp_pvt, OBJ_POINTER))) { + res = pvt->callno; + ao2_ref(pvt, -1); + pvt = NULL; + return res; + } + } + /* Look for an existing connection first */ for (x=1;(res < 1) && (x<maxnontrunkcall);x++) { ast_mutex_lock(&iaxsl[x]); @@ -1148,6 +1338,9 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc iaxs[x]->amaflags = amaflags; ast_copy_flags(iaxs[x], (&globalflags), IAX_NOTRANSFER | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF); ast_copy_string(iaxs[x]->accountcode, accountcode, sizeof(iaxs[x]->accountcode)); + if (iaxs[x]->peercallno) { + store_by_peercallno(iaxs[x]); + } } else { ast_log(LOG_WARNING, "Out of resources\n"); ast_mutex_unlock(&iaxsl[x]); @@ -1159,13 +1352,6 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc return res; } -static void iax2_frame_free(struct iax_frame *fr) -{ - if (fr->retrans > -1) - ast_sched_del(sched, fr->retrans); - iax_frame_free(fr); -} - static int iax2_queue_frame(int callno, struct ast_frame *f) { /* Assumes lock for callno is already held... */ @@ -1639,112 +1825,7 @@ static int iax2_predestroy_nolock(int callno) return res; } -static void iax2_destroy(int callno) -{ - struct chan_iax2_pvt *pvt; - struct iax_frame *cur; - struct ast_channel *owner; - struct iax2_user *user; -retry: - ast_mutex_lock(&iaxsl[callno]); - pvt = iaxs[callno]; - gettimeofday(&lastused[callno], NULL); - - if (pvt) - owner = pvt->owner; - else - owner = NULL; - if (owner) { - if (ast_mutex_trylock(&owner->lock)) { - ast_log(LOG_NOTICE, "Avoiding IAX destroy deadlock\n"); - ast_mutex_unlock(&iaxsl[callno]); - usleep(1); - goto retry; - } - } - if (!owner) - iaxs[callno] = NULL; - if (pvt) { - if (!owner) - pvt->owner = NULL; - if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) { - ast_mutex_lock(&userl.lock); - user = userl.users; - while (user) { - if (!strcmp(user->name, pvt->username)) { - user->curauthreq--; - break; - } - user = user->next; - } - ast_mutex_unlock(&userl.lock); - } - /* No more pings or lagrq's */ - if (pvt->pingid > -1) - ast_sched_del(sched, pvt->pingid); - if (pvt->lagid > -1) - ast_sched_del(sched, pvt->lagid); - if (pvt->autoid > -1) - ast_sched_del(sched, pvt->autoid); - if (pvt->authid > -1) - ast_sched_del(sched, pvt->authid); - if (pvt->initid > -1) - ast_sched_del(sched, pvt->initid); -#ifdef NEWJB - if (pvt->jbid > -1) - ast_sched_del(sched, pvt->jbid); - pvt->jbid = -1; -#endif - pvt->pingid = -1; - pvt->lagid = -1; - pvt->autoid = -1; - pvt->authid = -1; - pvt->initid = -1; - if (pvt->bridgetrans) - ast_translator_free_path(pvt->bridgetrans); - pvt->bridgetrans = NULL; - - /* Already gone */ - ast_set_flag(pvt, IAX_ALREADYGONE); - - if (owner) { - /* If there's an owner, prod it to give up */ - owner->_softhangup |= AST_SOFTHANGUP_DEV; - ast_queue_hangup(owner); - } - - for (cur = iaxq.head; cur ; cur = cur->next) { - /* Cancel any pending transmissions */ - if (cur->callno == pvt->callno) - cur->retries = -1; - } - if (pvt->reg) { - pvt->reg->callno = 0; - } - if (!owner) { - if (pvt->vars) { - ast_variables_destroy(pvt->vars); - pvt->vars = NULL; - } -#ifdef NEWJB - { - jb_frame frame; - while(jb_getall(pvt->jb,&frame) == JB_OK) - iax2_frame_free(frame.data); - jb_destroy(pvt->jb); - } -#endif - free(pvt); - } - } - if (owner) { - ast_mutex_unlock(&owner->lock); - } - ast_mutex_unlock(&iaxsl[callno]); - if (callno & 0x4000) - update_max_trunk(); -} static void iax2_destroy_nolock(int callno) { /* Actually it's easier to unlock, kill it, and relock */ @@ -5578,7 +5659,13 @@ static int complete_transfer(int callno, struct iax_ies *ies) pvt->rseqno = 0; pvt->iseqno = 0; pvt->aseqno = 0; + + if (pvt->peercallno) { + remove_by_peercallno(pvt); + } pvt->peercallno = peercallno; + store_by_peercallno(pvt); + pvt->transferring = TRANSFER_NONE; pvt->svoiceformat = -1; pvt->voiceformat = 0; @@ -6738,8 +6825,18 @@ static int socket_read(int *id, int fd, short events, void *cbdata) if (!inaddrcmp(&sin, &iaxs[fr->callno]->addr) && !minivid && f.subclass != IAX_COMMAND_TXCNT && /* for attended transfer */ - f.subclass != IAX_COMMAND_TXACC) /* for attended transfer */ - iaxs[fr->callno]->peercallno = (unsigned short)(ntohs(mh->callno) & ~IAX_FLAG_FULL); + f.subclass != IAX_COMMAND_TXACC) { /* for attended transfer */ + unsigned short new_peercallno; + + new_peercallno = (unsigned short) (ntohs(mh->callno) & ~IAX_FLAG_FULL); + if (new_peercallno && new_peercallno != iaxs[fr->callno]->peercallno) { + if (iaxs[fr->callno]->peercallno) { + remove_by_peercallno(iaxs[fr->callno]); + } + iaxs[fr->callno]->peercallno = new_peercallno; + store_by_peercallno(iaxs[fr->callno]); + } + } if (ntohs(mh->callno) & IAX_FLAG_FULL) { if (option_debug && iaxdebug) ast_log(LOG_DEBUG, "Received packet %d, (%d, %d)\n", fh->oseqno, f.frametype, f.subclass); @@ -9858,6 +9955,8 @@ static int __unload_module(void) for (x = 0; x < IAX_MAX_CALLS; x++) ast_mutex_destroy(&iaxsl[x]); + ao2_ref(iax_peercallno_pvts, -1); + return 0; } @@ -9867,6 +9966,23 @@ int unload_module() return __unload_module(); } +static int pvt_hash_cb(const void *obj, const int flags) +{ + const struct chan_iax2_pvt *pvt = obj; + + return pvt->peercallno; +} + +static int pvt_cmp_cb(void *obj, void *arg, int flags) +{ + struct chan_iax2_pvt *pvt = obj, *pvt2 = arg; + + /* The frames_received field is used to hold whether we're matching + * against a full frame or not ... */ + + return match(&pvt2->addr, pvt2->peercallno, pvt2->callno, pvt, + pvt2->frames_received) ? CMP_MATCH : 0; +} /*--- load_module: Load IAX2 module, load configuraiton ---*/ int load_module(void) @@ -9955,6 +10071,11 @@ int load_module(void) ast_netsock_release(outsock); } + iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb); + if (!iax_peercallno_pvts) { + res = -1; + } + ast_mutex_lock(®_lock); for (reg = registrations; reg; reg = reg->next) iax2_do_register(reg); |