aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--channels/chan_iax2.c87
1 files changed, 78 insertions, 9 deletions
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index 11d55d364..d6deac779 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -820,6 +820,15 @@ struct chan_iax2_pvt {
int frames_received;
/*! num bytes used for calltoken ie, even an empty ie should contain 2 */
unsigned char calltoken_ie_len;
+ /*! hold all signaling frames from the pbx thread until we have a destination callno */
+ char hold_signaling;
+ /*! frame queue for signaling frames from pbx thread waiting for destination callno */
+ AST_LIST_HEAD_NOLOCK(signaling_queue, signaling_queue_entry) signaling_queue;
+};
+
+struct signaling_queue_entry {
+ struct ast_frame f;
+ AST_LIST_ENTRY(signaling_queue_entry) next;
};
/*! table of available call numbers */
@@ -1703,10 +1712,56 @@ static int scheduled_destroy(const void *vid)
return 0;
}
+static void free_signaling_queue_entry(struct signaling_queue_entry *s)
+{
+ ast_free(s->f.data.ptr);
+ ast_free(s);
+}
+
+/*! \brief This function must be called once we are sure the other side has
+ * given us a call number. All signaling is held here until that point. */
+static void send_signaling(struct chan_iax2_pvt *pvt)
+{
+ struct signaling_queue_entry *s = NULL;
+
+ while ((s = AST_LIST_REMOVE_HEAD(&pvt->signaling_queue, next))) {
+ iax2_send(pvt, &s->f, 0, -1, 0, 0, 0);
+ free_signaling_queue_entry(s);
+ }
+ pvt->hold_signaling = 0;
+}
+
+/*! \brief All frames other than that of type AST_FRAME_IAX must be held until
+ * we have received a destination call number. */
+static int queue_signalling(struct chan_iax2_pvt *pvt, struct ast_frame *f)
+{
+ struct signaling_queue_entry *new;
+
+ if (f->frametype == AST_FRAME_IAX || !pvt->hold_signaling) {
+ return 1; /* do not queue this frame */
+ } else if (!(new = ast_calloc(1, sizeof(struct signaling_queue_entry)))) {
+ return -1; /* out of memory */
+ }
+
+ memcpy(&new->f, f, sizeof(new->f)); /* copy ast_frame into our queue entry */
+
+ if (new->f.datalen) { /* if there is data in this frame copy it over as well */
+ if (!(new->f.data.ptr = ast_calloc(1, new->f.datalen))) {
+ free_signaling_queue_entry(new);
+ return -1;
+ }
+ memcpy(new->f.data.ptr, f->data.ptr, sizeof(*new->f.data.ptr));
+ }
+ AST_LIST_INSERT_TAIL(&pvt->signaling_queue, new, next);
+
+ return 0;
+}
+
static void pvt_destructor(void *obj)
{
struct chan_iax2_pvt *pvt = obj;
struct iax_frame *cur = NULL;
+ struct signaling_queue_entry *s = NULL;
ast_mutex_lock(&iaxsl[pvt->callno]);
@@ -1725,6 +1780,10 @@ static void pvt_destructor(void *obj)
ast_mutex_unlock(&iaxsl[pvt->callno]);
+ while ((s = AST_LIST_REMOVE_HEAD(&pvt->signaling_queue, next))) {
+ free_signaling_queue_entry(s);
+ }
+
if (pvt->reg) {
pvt->reg->callno = 0;
}
@@ -1781,6 +1840,9 @@ static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, const char *host)
AST_LIST_HEAD_INIT_NOLOCK(&tmp->dpentries);
+ tmp->hold_signaling = 1;
+ AST_LIST_HEAD_INIT_NOLOCK(&tmp->signaling_queue);
+
return tmp;
}
@@ -7148,6 +7210,7 @@ static int __send_command(struct chan_iax2_pvt *i, char type, int command, unsig
int now, int transfer, int final)
{
struct ast_frame f = { 0, };
+ int res = 0;
f.frametype = type;
f.subclass = command;
@@ -7155,6 +7218,10 @@ static int __send_command(struct chan_iax2_pvt *i, char type, int command, unsig
f.src = __FUNCTION__;
f.data.ptr = (void *) data;
+ if ((res = queue_signalling(i, &f)) <= 0) {
+ return res;
+ }
+
return iax2_send(i, &f, ts, seqno, now, transfer, final);
}
@@ -9634,19 +9701,15 @@ static int socket_process(struct iax2_thread *thread)
int check_dcallno = 0;
/*
- * We enforce accurate destination call numbers for all full frames except
- * LAGRQ and PING commands. This is because older versions of Asterisk
- * schedule these commands to get sent very quickly, and they will sometimes
- * be sent before they receive the first frame from the other side. When
- * that happens, it doesn't contain the destination call number. However,
- * not checking it for these frames is safe.
- *
+ * We enforce accurate destination call numbers for ACKs. This forces the other
+ * end to know the destination call number before call setup can complete.
+ *
* Discussed in the following thread:
* http://lists.digium.com/pipermail/asterisk-dev/2008-May/033217.html
*/
- if (ntohs(mh->callno) & IAX_FLAG_FULL) {
- check_dcallno = f.frametype == AST_FRAME_IAX ? (f.subclass != IAX_COMMAND_PING && f.subclass != IAX_COMMAND_LAGRQ) : 1;
+ if ((ntohs(mh->callno) & IAX_FLAG_FULL) && ((f.frametype == AST_FRAME_IAX) && (f.subclass == IAX_COMMAND_ACK))) {
+ check_dcallno = 1;
}
if (!(fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, fd, check_dcallno))) {
@@ -9922,6 +9985,12 @@ static int socket_process(struct iax2_thread *thread)
}
}
+ /* once we receive our first IAX Full Frame that is not CallToken related, send all
+ * queued signaling frames that were being held. */
+ if ((f.frametype == AST_FRAME_IAX) && (f.subclass != IAX_COMMAND_CALLTOKEN) && iaxs[fr->callno]->hold_signaling) {
+ send_signaling(iaxs[fr->callno]);
+ }
+
if (f.frametype == AST_FRAME_VOICE) {
if (f.subclass != iaxs[fr->callno]->voiceformat) {
iaxs[fr->callno]->voiceformat = f.subclass;