diff --git a/channels/chan_console.c b/channels/chan_console.c index 23931a8c34bfc1d850d05a306e2f6b287b46198d..4c8576db5e802840ed95a63d40d89aaaf02f4445 100644 --- a/channels/chan_console.c +++ b/channels/chan_console.c @@ -393,7 +393,7 @@ return_unlock: static int stop_stream(struct console_pvt *pvt) { - if (!pvt->streamstate || pvt->thread == AST_PTHREADT_NULL) + if (!pvt->streamstate) return 0; pthread_cancel(pvt->thread); diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index bd2a86f067a7b0e6d390b1d790b02b555a31c3af..a41f6cf3f1327fb5e09c5b125b248f09ec3c7064 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -87,6 +87,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/netsock.h" #include "asterisk/stringfields.h" #include "asterisk/linkedlists.h" +#include "asterisk/dlinkedlists.h" #include "asterisk/event.h" #include "asterisk/astobj2.h" @@ -639,6 +640,8 @@ struct chan_iax2_pvt { int frames_dropped; /*! received frame count: (just for stats) */ int frames_received; + + AST_DLLIST_ENTRY(chan_iax2_pvt) entry; }; /*! @@ -838,6 +841,17 @@ static void __attribute__((format (printf, 1, 2))) jb_debug_output(const char *f */ static struct chan_iax2_pvt *iaxs[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; + /*! * \brief chan_iax2_pvt structure locks * @@ -846,6 +860,7 @@ static struct chan_iax2_pvt *iaxs[IAX_MAX_CALLS]; * local call number for the associated pvt struct. */ static ast_mutex_t iaxsl[ARRAY_LEN(iaxs)]; + /*! * \brief The last time a call number was used * @@ -1329,16 +1344,88 @@ static int iax2_getpeername(struct sockaddr_in sin, char *host, int len) return res; } +static void iax2_destroy_helper(struct chan_iax2_pvt *pvt) +{ + /* Decrement AUTHREQ count if needed */ + if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) { + 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_clear_flag(pvt, IAX_MAXAUTHREQ); + } + /* No more pings or lagrq's */ + AST_SCHED_DEL(sched, pvt->pingid); + AST_SCHED_DEL(sched, pvt->lagid); + AST_SCHED_DEL(sched, pvt->autoid); + AST_SCHED_DEL(sched, pvt->authid); + AST_SCHED_DEL(sched, pvt->initid); + AST_SCHED_DEL(sched, pvt->jbid); +} + +static void iax2_frame_free(struct iax_frame *fr) +{ + 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 = NULL; + + iax2_destroy_helper(pvt); + + /* Already gone */ + ast_set_flag(pvt, IAX_ALREADYGONE); + + AST_LIST_LOCK(&frame_queue); + AST_LIST_TRAVERSE(&frame_queue, cur, list) { + /* Cancel any pending transmissions */ + if (cur->callno == pvt->callno) { + cur->retries = -1; + } + } + AST_LIST_UNLOCK(&frame_queue); + + if (pvt->reg) { + pvt->reg->callno = 0; + } + + if (!pvt->owner) { + jb_frame frame; + if (pvt->vars) { + ast_variables_destroy(pvt->vars); + pvt->vars = NULL; + } + + while (jb_getall(pvt->jb, &frame) == JB_OK) { + iax2_frame_free(frame.data); + } + + jb_destroy(pvt->jb); + ast_string_field_free_memory(pvt); + } +} + static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, const char *host) { struct chan_iax2_pvt *tmp; jb_conf jbconf; - if (!(tmp = ast_calloc(1, sizeof(*tmp)))) + if (!(tmp = ao2_alloc(sizeof(*tmp), pvt_destructor))) { return NULL; + } if (ast_string_field_init(tmp, 32)) { - ast_free(tmp); + ao2_ref(tmp, -1); tmp = NULL; return NULL; } @@ -1481,6 +1568,26 @@ static int make_trunk(unsigned short callno, int locked) return res; } +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); +} + /*! * \todo XXX Note that this function contains a very expensive operation that * happens for *every* incoming media frame. It iterates through every @@ -1505,6 +1612,28 @@ static int __find_callno(unsigned short callno, unsigned short dcallno, struct s 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))) { + if (return_locked) { + ast_mutex_lock(&iaxsl[pvt->callno]); + } + res = pvt->callno; + ao2_ref(pvt, -1); + pvt = NULL; + return res; + } + } + for (x = 1; !res && x < maxnontrunkcall; x++) { ast_mutex_lock(&iaxsl[x]); if (iaxs[x]) { @@ -1587,6 +1716,10 @@ static int __find_callno(unsigned short callno, unsigned short dcallno, struct s ast_string_field_set(iaxs[x], mohinterpret, mohinterpret); ast_string_field_set(iaxs[x], mohsuggest, mohsuggest); ast_string_field_set(iaxs[x], parkinglot, default_parkinglot); + + if (iaxs[x]->peercallno) { + store_by_peercallno(iaxs[x]); + } } else { ast_log(LOG_WARNING, "Out of resources\n"); ast_mutex_unlock(&iaxsl[x]); @@ -1609,12 +1742,6 @@ static int find_callno_locked(unsigned short callno, unsigned short dcallno, str return __find_callno(callno, dcallno, sin, new, sockfd, 1, full_frame); } -static void iax2_frame_free(struct iax_frame *fr) -{ - AST_SCHED_DEL(sched, fr->retrans); - iax_frame_free(fr); -} - /*! * \brief Queue a frame to a call's owning asterisk channel * @@ -2064,32 +2191,6 @@ static int send_packet(struct iax_frame *f) return res; } -static void iax2_destroy_helper(struct chan_iax2_pvt *pvt) -{ - /* Decrement AUTHREQ count if needed */ - if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) { - 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_clear_flag(pvt, IAX_MAXAUTHREQ); - } - /* No more pings or lagrq's */ - AST_SCHED_DEL(sched, pvt->pingid); - AST_SCHED_DEL(sched, pvt->lagid); - AST_SCHED_DEL(sched, pvt->autoid); - AST_SCHED_DEL(sched, pvt->authid); - AST_SCHED_DEL(sched, pvt->initid); - AST_SCHED_DEL(sched, pvt->jbid); -} - /*! * \note Since this function calls iax2_queue_hangup(), the pvt struct * for the given call number may disappear during its execution. @@ -2120,7 +2221,6 @@ static int iax2_predestroy(int callno) static void iax2_destroy(int callno) { struct chan_iax2_pvt *pvt = NULL; - struct iax_frame *cur = NULL; struct ast_channel *owner = NULL; retry: @@ -2138,53 +2238,38 @@ retry: goto retry; } } - if (!owner) + + if (!owner) { iaxs[callno] = NULL; + } + if (pvt) { - if (!owner) + if (!owner) { pvt->owner = NULL; - iax2_destroy_helper(pvt); - - /* Already gone */ - ast_set_flag(pvt, IAX_ALREADYGONE); - - if (owner) { + } else { /* If there's an owner, prod it to give up */ /* It is ok to use ast_queue_hangup() here instead of iax2_queue_hangup() * because we already hold the owner channel lock. */ ast_queue_hangup(owner, -1); } - AST_LIST_LOCK(&frame_queue); - AST_LIST_TRAVERSE(&frame_queue, cur, list) { - /* Cancel any pending transmissions */ - if (cur->callno == pvt->callno) - cur->retries = -1; + if (pvt->peercallno) { + remove_by_peercallno(pvt); } - AST_LIST_UNLOCK(&frame_queue); - if (pvt->reg) - pvt->reg->callno = 0; if (!owner) { - jb_frame frame; - if (pvt->vars) { - ast_variables_destroy(pvt->vars); - pvt->vars = NULL; - } - - while (jb_getall(pvt->jb, &frame) == JB_OK) - iax2_frame_free(frame.data); - jb_destroy(pvt->jb); - /* gotta free up the stringfields */ - ast_string_field_free_memory(pvt); - ast_free(pvt); + ao2_ref(pvt, -1); + pvt = NULL; } } + if (owner) { ast_channel_unlock(owner); } - if (callno & 0x4000) + + if (callno & 0x4000) { update_max_trunk(); + } } static int update_packet(struct iax_frame *f) @@ -6386,7 +6471,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; @@ -7904,7 +7995,16 @@ static int socket_process(struct iax2_thread *thread) 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); + 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 (iaxdebug) @@ -11782,6 +11882,7 @@ static int __unload_module(void) ao2_ref(peers, -1); ao2_ref(users, -1); + ao2_ref(iax_peercallno_pvts, -1); con = ast_context_find(regcontext); if (con) @@ -11807,6 +11908,24 @@ static int peer_set_sock_cb(void *obj, void *arg, int flags) return 0; } +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; +} + /*! \brief Load IAX2 module, load configuraiton ---*/ static int load_module(void) { @@ -11822,6 +11941,12 @@ static int load_module(void) ao2_ref(peers, -1); return AST_MODULE_LOAD_FAILURE; } + iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb); + if (!iax_peercallno_pvts) { + ao2_ref(peers, -1); + ao2_ref(users, -1); + return AST_MODULE_LOAD_FAILURE; + } ast_custom_function_register(&iaxpeer_function); ast_custom_function_register(&iaxvar_function);