Version 0.3.0 from FTP
authorMark Spencer <markster@digium.com>
Fri, 17 Jan 2003 19:50:12 +0000 (19:50 +0000)
committerMark Spencer <markster@digium.com>
Fri, 17 Jan 2003 19:50:12 +0000 (19:50 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@586 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_iax.c
configs/sip.conf.sample
include/asterisk/rtp.h

index 7f8e233..71ca1e7 100755 (executable)
@@ -28,6 +28,7 @@
 #include <asterisk/cdr.h>
 #include <asterisk/crypto.h>
 #include <asterisk/acl.h>
+#include <asterisk/manager.h>
 #include <arpa/inet.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -74,7 +75,7 @@ static int lagrq_time = 10;
 static int nextcallno = 0;
 static int maxjitterbuffer=3000;
 
-static int iaxdefaultdpcache=30 * 60;  /* Cache dialplan entries for 30 minutes by default */
+static int iaxdefaultdpcache=10 * 60;  /* Cache dialplan entries for 10 minutes by default */
 
 static int iaxdefaulttimeout = 5;              /* Default to wait no more than 5 seconds for a reply to come back */
 
@@ -86,7 +87,6 @@ static int expirey = AST_DEFAULT_REG_EXPIRE;
 
 static int usecnt;
 static pthread_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER;
-static pthread_mutex_t iaxs_lock = AST_MUTEX_INITIALIZER;
 
 int (*regfunk)(char *username, int onoff) = NULL;
 
@@ -105,6 +105,11 @@ int (*regfunk)(char *username, int onoff) = NULL;
 #define IAX_CAPABILITY_LOWFREE         (IAX_CAPABILITY_LOWBANDWIDTH & \
                                                                         ~AST_FORMAT_G723_1)
 
+
+#define DEFAULT_MAXMS          2000            /* Must be faster than 2 seconds by default */
+#define DEFAULT_FREQ_OK                60 * 1000               /* How often to check for the host to be up */
+#define DEFAULT_FREQ_NOTOK     10 * 1000               /* How often to check, if the host is down... */
+
 static struct io_context *io;
 static struct sched_context *sched;
 
@@ -170,6 +175,13 @@ struct iax_peer {
        int expirey;                                    /* How soon to expire */
        int capability;                                 /* Capability */
        int delme;                                              /* I need to be deleted */
+
+       /* Qualification */
+       int callno;                                     /* Call number of POKE request */
+       int pokeexpire;                                 /* When to expire poke */
+       int lastms;                                     /* How long last response took (in ms), or -1 for no response */
+       int maxms;                                      /* Max ms we will accept for the host to be up, 0 to not monitor */
+       
        struct ast_ha *ha;
        struct iax_peer *next;
 };
@@ -229,6 +241,8 @@ struct chan_iax_pvt {
        unsigned int lastsent;
        /* Ping time */
        unsigned int pingtime;
+       /* Max time for initial response */
+       int maxtime;
        /* Peer Address */
        struct sockaddr_in addr;
        /* Our call number */
@@ -295,6 +309,8 @@ struct chan_iax_pvt {
        char language[80];
        /* Associated registry */
        struct iax_registry *reg;
+       /* Associated peer for poking */
+       struct iax_peer *peerpoke;
 
        /* Transferring status */
        int transferring;
@@ -310,9 +326,10 @@ struct chan_iax_pvt {
        
        /* Who we are bridged to */
        int bridgecallno;
-       int pingid;
-       int lagid;
-       int autoid;
+       int pingid;                     /* Transmit PING request */
+       int lagid;                      /* Retransmit lag request */
+       int autoid;                     /* Auto hangup for Dialplan requestor */
+       int initid;                     /* Initial peer auto-congest ID (based on qualified peers) */
        char dproot[AST_MAX_EXTENSION];
        char accountcode[20];
        int amaflags;
@@ -389,6 +406,8 @@ static struct ast_peer_list {
 #define CACHE_FLAG_TRANSMITTED (1 << 5)
 /* Timeout */
 #define CACHE_FLAG_UNKNOWN             (1 << 6)
+/* Matchmore */
+#define CACHE_FLAG_MATCHMORE   (1 << 7)
 
 static struct iax_dpcache {
        char peercontext[AST_MAX_EXTENSION];
@@ -446,6 +465,9 @@ void showframe(struct ast_iax_frame *f, struct ast_iax_full_hdr *fhi, int rx, st
                "TXREADY",
                "TXREL  ",
                "TXREJ  ",
+               "QUELCH ",
+               "UNQULCH",
+               "POKE",
        };
        char *cmds[] = {
                "(0?)",
@@ -514,6 +536,7 @@ void showframe(struct ast_iax_frame *f, struct ast_iax_full_hdr *fhi, int rx, st
 
 /* XXX We probably should use a mutex when working with this XXX */
 static struct chan_iax_pvt *iaxs[AST_IAX_MAX_CALLS];
+static pthread_mutex_t iaxsl[AST_IAX_MAX_CALLS];
 
 static int send_command(struct chan_iax_pvt *, char, int, unsigned int, char *, int, int);
 static int send_command_immediate(struct chan_iax_pvt *, char, int, unsigned int, char *, int, int);
@@ -573,8 +596,13 @@ static unsigned char compress_subclass(int subclass)
 static int uncompress_subclass(unsigned char csub)
 {
        /* If the SC_LOG flag is set, return 2^csub otherwise csub */
-       if (csub & AST_FLAG_SC_LOG)
-               return 1 << (csub & ~AST_FLAG_SC_LOG & AST_MAX_SHIFT);
+       if (csub & AST_FLAG_SC_LOG) {
+               /* special case for 'compressed' -1 */
+               if (csub == 0xff)
+                       return -1;
+               else
+                       return 1 << (csub & ~AST_FLAG_SC_LOG & AST_MAX_SHIFT);
+       }
        else
                return csub;
 }
@@ -592,39 +620,43 @@ static struct chan_iax_pvt *new_iax(void)
                tmp->pingid = -1;
                tmp->lagid = -1;
                tmp->autoid = -1;
+               tmp->initid = -1;
                /* strncpy(tmp->context, context, sizeof(tmp->context)-1); */
                strncpy(tmp->exten, "s", sizeof(tmp->exten)-1);
        }
        return tmp;
 }
 
-static int get_timelen(struct ast_frame *f)
+static int get_samples(struct ast_frame *f)
 {
-       int timelen=0;
+       int samples=0;
        switch(f->subclass) {
        case AST_FORMAT_G723_1:
-               timelen = 30 /* XXX Not necessarily true XXX */;
+               samples = 240 /* XXX Not necessarily true XXX */;
                break;
        case AST_FORMAT_GSM:
-               timelen = 20 * (f->datalen / 20);
+               samples = 160 * (f->datalen / 33);
                break;
        case AST_FORMAT_SLINEAR:
-               timelen = f->datalen / 16;
+               samples = f->datalen / 2;
                break;
        case AST_FORMAT_LPC10:
-               timelen = 22;
-               timelen += ((char *)(f->data))[7] & 0x1;
+               samples = 22 * 8;
+               samples += (((char *)(f->data))[7] & 0x1) * 8;
                break;
        case AST_FORMAT_ULAW:
-               timelen = f->datalen / 8;
+               samples = f->datalen;
+               break;
+       case AST_FORMAT_ALAW:
+               samples = f->datalen;
                break;
        case AST_FORMAT_ADPCM:
-               timelen = f->datalen / 4;
+               samples = f->datalen *2;
                break;
        default:
-               ast_log(LOG_WARNING, "Don't know how to calculate timelen on %d packets\n", f->subclass);
+               ast_log(LOG_WARNING, "Don't know how to calculate samples on %d packets\n", f->subclass);
        }
-       return timelen;
+       return samples;
 }
 
 static int frames = 0;
@@ -720,14 +752,15 @@ static int find_callno(short callno, short dcallno ,struct sockaddr_in *sin, int
        int start;
        if (new <= NEW_ALLOW) {
                /* Look for an existing connection first */
-               for (x=0;x<AST_IAX_MAX_CALLS;x++) {
+               for (x=0;(res < 0) && (x<AST_IAX_MAX_CALLS);x++) {
+                       ast_pthread_mutex_lock(&iaxsl[x]);
                        if (iaxs[x]) {
                                /* Look for an exact match */
                                if (match(sin, callno, dcallno, iaxs[x])) {
                                        res = x;
-                                       break;
                                }
                        }
+                       ast_pthread_mutex_unlock(&iaxsl[x]);
                }
        }
        if ((res < 0) && (new >= NEW_ALLOW)) {
@@ -738,7 +771,9 @@ static int find_callno(short callno, short dcallno ,struct sockaddr_in *sin, int
                        ast_log(LOG_WARNING, "Unable to accept more calls\n");
                        return -1;
                }
+               ast_pthread_mutex_lock(&iaxsl[x]);
                iaxs[x] = new_iax();
+               ast_pthread_mutex_unlock(&iaxsl[x]);
                if (iaxs[x]) {
                        if (option_debug)
                                ast_log(LOG_DEBUG, "Creating new call structure %d\n", x);
@@ -763,11 +798,39 @@ static int find_callno(short callno, short dcallno ,struct sockaddr_in *sin, int
        return res;
 }
 
+static int iax_queue_frame(int callno, struct ast_frame *f)
+{
+       int pass =0;
+       /* Assumes lock for callno is already held... */
+       for (;;) {
+               pass++;
+               if (!pthread_mutex_trylock(&iaxsl[callno])) {
+                       ast_log(LOG_WARNING, "Lock is not held on pass %d of iax_queue_frame\n", pass);
+                       CRASH;
+               }
+               if (iaxs[callno] && iaxs[callno]->owner) {
+                       if (pthread_mutex_trylock(&iaxs[callno]->owner->lock)) {
+                               /* Avoid deadlock by pausing and trying again */
+                               ast_pthread_mutex_unlock(&iaxsl[callno]);
+                               usleep(1);
+                               ast_pthread_mutex_lock(&iaxsl[callno]);
+                       } else {
+                               ast_queue_frame(iaxs[callno]->owner, f, 0);
+                               ast_pthread_mutex_unlock(&iaxs[callno]->owner->lock);
+                               break;
+                       }
+               } else
+                       break;
+       }
+       return 0;
+}
+
 static int iax_send(struct chan_iax_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno, int now, int transfer, int final);
 
-static int do_deliver(void *data)
+static int __do_deliver(void *data)
 {
-       /* Just deliver the packet by using queueing */
+       /* Just deliver the packet by using queueing.  This is called by
+         the IAX thread with the iaxsl lock held. */
        struct ast_iax_frame *fr = data;
        unsigned int ts;
        fr->retrans = -1;
@@ -785,9 +848,7 @@ static int do_deliver(void *data)
                                iaxs[fr->callno]->lag = ts - fr->ts;
                        }
                } else {
-                       if (iaxs[fr->callno]->owner) {
-                               ast_queue_frame(iaxs[fr->callno]->owner, fr->f, 1);
-                       }
+                       iax_queue_frame(fr->callno, fr->f);
                }
        }
        /* Free the packet */
@@ -798,6 +859,18 @@ static int do_deliver(void *data)
        return 0;
 }
 
+static int do_deliver(void *data)
+{
+       /* Locking version of __do_deliver */
+       struct ast_iax_frame *fr = data;
+       int callno = fr->callno;
+       int res;
+       ast_pthread_mutex_lock(&iaxsl[callno]);
+       res = __do_deliver(data);
+       ast_pthread_mutex_unlock(&iaxsl[callno]);
+       return res;
+}
+
 static int handle_error()
 {
        /* XXX Ideally we should figure out why an error occured and then abort those
@@ -835,6 +908,7 @@ static int handle_error()
 static int send_packet(struct ast_iax_frame *f)
 {
        int res;
+       /* Called with iaxsl held */
        if (option_debug)
                ast_log(LOG_DEBUG, "Sending %d on %d/%d to %s:%d\n", f->ts, f->callno, iaxs[f->callno]->peercallno, inet_ntoa(iaxs[f->callno]->addr.sin_addr), ntohs(iaxs[f->callno]->addr.sin_port));
        /* Don't send if there was an error, but return error instead */
@@ -871,11 +945,82 @@ static int send_packet(struct ast_iax_frame *f)
 }
 
 
+static int iax_predestroy(int callno)
+{
+       struct ast_channel *c;
+       struct chan_iax_pvt *pvt;
+       ast_pthread_mutex_lock(&iaxsl[callno]);
+       pvt = iaxs[callno];
+       if (!pvt) {
+               ast_pthread_mutex_unlock(&iaxsl[callno]);
+               return -1;
+       }
+       if (!pvt->alreadygone) {
+               /* 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->initid > -1)
+                       ast_sched_del(sched, pvt->initid);
+               pvt->pingid = -1;
+               pvt->lagid = -1;
+               pvt->autoid = -1;
+               pvt->initid = -1;
+               pvt->alreadygone = 1;
+       }
+       c = pvt->owner;
+       if (c) {
+               c->_softhangup |= AST_SOFTHANGUP_DEV;
+               c->pvt->pvt = NULL;
+               pvt->owner = NULL;
+               ast_pthread_mutex_lock(&usecnt_lock);
+               usecnt--;
+               if (usecnt < 0) 
+                       ast_log(LOG_WARNING, "Usecnt < 0???\n");
+               ast_pthread_mutex_unlock(&usecnt_lock);
+               ast_update_use_count();
+       }
+       ast_pthread_mutex_unlock(&iaxsl[callno]);
+       return 0;
+}
+
+static int iax_predestroy_nolock(int callno)
+{
+       int res;
+       ast_pthread_mutex_unlock(&iaxsl[callno]);
+       res = iax_predestroy(callno);
+       ast_pthread_mutex_lock(&iaxsl[callno]);
+       return res;
+}
+
 static void iax_destroy(int callno)
 {
-       struct chan_iax_pvt *pvt = iaxs[callno];
+       struct chan_iax_pvt *pvt;
        struct ast_iax_frame *cur;
+       struct ast_channel *owner;
+
+retry:
+       ast_pthread_mutex_lock(&iaxsl[callno]);
+       pvt = iaxs[callno];
+       iaxs[callno] = NULL;
+
+       if (pvt)
+               owner = pvt->owner;
+       else
+               owner = NULL;
+       if (owner) {
+               if (pthread_mutex_trylock(&owner->lock)) {
+                       ast_log(LOG_NOTICE, "Avoiding IAX destroy deadlock\n");
+                       ast_pthread_mutex_unlock(&iaxsl[callno]);
+                       usleep(1);
+                       goto retry;
+               }
+       }
        if (pvt) {
+               pvt->owner = NULL;
                /* No more pings or lagrq's */
                if (pvt->pingid > -1)
                        ast_sched_del(sched, pvt->pingid);
@@ -883,29 +1028,23 @@ static void iax_destroy(int callno)
                        ast_sched_del(sched, pvt->lagid);
                if (pvt->autoid > -1)
                        ast_sched_del(sched, pvt->autoid);
+               if (pvt->initid > -1)
+                       ast_sched_del(sched, pvt->initid);
                pvt->pingid = -1;
                pvt->lagid = -1;
                pvt->autoid = -1;
+               pvt->initid = -1;
 
                /* Already gone */
                pvt->alreadygone = 1;
 
-               if (pvt->owner) {
-                       if (ast_pthread_mutex_lock(&pvt->owner->lock)) {
-                               ast_log(LOG_WARNING, "Unable to lock channel %s\n", pvt->owner->name);
-                       } else if (!pvt->owner || (pvt->owner->pvt->pvt != pvt)) {
-                               ast_log(LOG_WARNING, "Something very strange happened...\n");
-                       } else {
-                               /* If there's an owner, prod it to give up */
-                               pvt->owner->pvt->pvt = NULL;
-                               pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV;
-                               ast_queue_hangup(pvt->owner, 0);
-                               ast_pthread_mutex_unlock(&pvt->owner->lock);
-                       }
+               if (owner) {
+                       /* If there's an owner, prod it to give up */
+                       owner->pvt->pvt = NULL;
+                       owner->_softhangup |= AST_SOFTHANGUP_DEV;
+                       ast_queue_hangup(owner, 0);
                }
 
-               iaxs[callno] = NULL;
-
                for (cur = iaxq.head; cur ; cur = cur->next) {
                        /* Cancel any pending transmissions */
                        if (cur->callno == pvt->callno) 
@@ -916,15 +1055,31 @@ static void iax_destroy(int callno)
                }
                free(pvt);
        }
+       if (owner) {
+               ast_pthread_mutex_unlock(&owner->lock);
+       }
+       ast_pthread_mutex_unlock(&iaxsl[callno]);
 }
+static void iax_destroy_nolock(int callno)
+{      
+       /* Actually it's easier to unlock, kill it, and relock */
+       ast_pthread_mutex_unlock(&iaxsl[callno]);
+       iax_destroy(callno);
+       ast_pthread_mutex_lock(&iaxsl[callno]);
+}
+
 
 
 static int attempt_transmit(void *data)
 {
-       /* Attempt to transmit the frame to the remote peer */
+       /* Attempt to transmit the frame to the remote peer...
+          Called without iaxsl held. */
        struct ast_iax_frame *f = data;
        int freeme=0;
+       int callno = f->callno;
        /* Make sure this call is still active */
+       if (callno > -1) 
+               ast_pthread_mutex_lock(&iaxsl[callno]);
        if ((f->callno > -1) && iaxs[f->callno]) {
                if ((f->retries < 0) /* Already ACK'd */ ||
                    (f->retries >= max_retries) /* Too many attempts */) {
@@ -935,22 +1090,24 @@ static int attempt_transmit(void *data)
                                                send_command(iaxs[f->callno], AST_FRAME_IAX, AST_IAX_COMMAND_TXREJ, 0, NULL, 0, -1);
                                        } else if (f->final) {
                                                if (f->final) 
-                                                       iax_destroy(f->callno);
+                                                       iax_destroy_nolock(f->callno);
                                        } else {
                                                if (iaxs[f->callno]->owner)
                                                        ast_log(LOG_WARNING, "Max retries exceeded to host %s on %s (type = %d, subclass = %d, ts=%d, seqno=%d)\n", inet_ntoa(iaxs[f->callno]->addr.sin_addr),iaxs[f->callno]->owner->name , f->f->frametype, f->f->subclass, f->ts, f->seqno);
                                                iaxs[f->callno]->error = ETIMEDOUT;
                                                if (iaxs[f->callno]->owner) {
+                                                       struct ast_frame fr = { 0, };
                                                        /* Hangup the fd */
-                                                       ast_softhangup(iaxs[f->callno]->owner, AST_SOFTHANGUP_DEV);
-                                                       ast_queue_hangup(iaxs[f->callno]->owner, 1);
+                                                       fr.frametype = AST_FRAME_CONTROL;
+                                                       fr.subclass = AST_CONTROL_HANGUP;
+                                                       iax_queue_frame(f->callno, &fr);
                                                } else {
                                                        if (iaxs[f->callno]->reg) {
                                                                memset(&iaxs[f->callno]->reg->us, 0, sizeof(iaxs[f->callno]->reg->us));
                                                                iaxs[f->callno]->reg->regstate = REG_STATE_TIMEOUT;
                                                                iaxs[f->callno]->reg->refresh = AST_DEFAULT_REG_EXPIRE;
                                                        }
-                                                       iax_destroy(f->callno);
+                                                       iax_destroy_nolock(f->callno);
                                                }
                                        }
 
@@ -974,6 +1131,8 @@ static int attempt_transmit(void *data)
                f->retries = -1;
                freeme++;
        }
+       if (callno > -1)
+               ast_pthread_mutex_unlock(&iaxsl[callno]);
        /* Do not try again */
        if (freeme) {
                /* Don't attempt delivery, just remove it from the queue */
@@ -1006,7 +1165,6 @@ static int iax_set_jitter(int fd, int argc, char *argv[])
                        max_jitter_buffer = 0;
        } else {
                if (argc == 5) {
-                       ast_pthread_mutex_lock(&iaxs_lock);
                        if ((atoi(argv[3]) >= 0) && (atoi(argv[3]) < AST_IAX_MAX_CALLS)) {
                                if (iaxs[atoi(argv[3])]) {
                                        iaxs[atoi(argv[3])]->jitterbuffer = atoi(argv[4]);
@@ -1016,7 +1174,6 @@ static int iax_set_jitter(int fd, int argc, char *argv[])
                                        ast_cli(fd, "No such call '%d'\n", atoi(argv[3]));
                        } else
                                ast_cli(fd, "%d is not a valid call number\n", atoi(argv[3]));
-                       ast_pthread_mutex_unlock(&iaxs_lock);
                }
        }
        return RESULT_SUCCESS;
@@ -1075,6 +1232,8 @@ static int iax_show_cache(int fd, int argc, char *argv[])
                        strcat(tmp, "TIMEOUT|");
                if (dp->flags & CACHE_FLAG_TRANSMITTED)
                        strcat(tmp, "TRANSMITTED|");
+               if (dp->flags & CACHE_FLAG_MATCHMORE)
+                       strcat(tmp, "MATCHMORE|");
                if (dp->flags & CACHE_FLAG_UNKNOWN)
                        strcat(tmp, "UNKNOWN|");
                /* Trim trailing pipe */
@@ -1147,7 +1306,8 @@ static int schedule_delivery(struct ast_iax_frame *fr, int reallydeliver)
        int drops[MEMORY_SIZE];
        int min, max=0, maxone=0,y,z, match;
        /* ms is a measure of the "lateness" of the packet relative to the first
-          packet we received, which always has a lateness of 1.  */
+          packet we received, which always has a lateness of 1.  Called by
+          IAX thread, with iaxsl lock held. */
        ms = calc_rxstamp(iaxs[fr->callno]) - fr->ts;
 
        if (ms > 32767) {
@@ -1254,7 +1414,7 @@ static int schedule_delivery(struct ast_iax_frame *fr, int reallydeliver)
                        ast_log(LOG_DEBUG, "Calculated ms is %d\n", ms);
                /* Don't deliver it more than 4 ms late */
                if ((ms > -4) || (fr->f->frametype != AST_FRAME_VOICE)) {
-                       do_deliver(fr);
+                       __do_deliver(fr);
                } else {
                        if (option_debug)
                                ast_log(LOG_DEBUG, "Dropping voice packet since %d ms is, too old\n", ms);
@@ -1328,13 +1488,15 @@ static int iax_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan
        return 0;
 }
 
-static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, char *peer, char *context)
+static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, int *maxtime, char *peer, char *context)
 {
        struct hostent *hp;
        struct iax_peer *p;
        int found=0;
        if (sendani)
                *sendani = 0;
+       if (maxtime)
+               *maxtime = 0;
        sin->sin_family = AF_INET;
        ast_pthread_mutex_lock(&peerl.lock);
        p = peerl.peers;
@@ -1343,9 +1505,12 @@ static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, c
                        found++;
                        if (capability)
                                *capability = p->capability;
-                       if (p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) {
+                       if ((p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) &&
+                               (!p->maxms || ((p->lastms > 0)  && (p->lastms <= p->maxms)))) {
                                if (sendani)
-                                       *sendani = p->sendani;
+                                       *sendani = p->sendani;          /* Whether we transmit ANI */
+                               if (maxtime)
+                                       *maxtime = p->maxms;            /* Max time they should take */
                                if (context)
                                        strncpy(context, p->context, AST_MAX_EXTENSION - 1);
                                if (p->addr.sin_addr.s_addr) {
@@ -1376,6 +1541,21 @@ static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, c
        else
                return 0;
 }
+
+static int auto_congest(void *nothing)
+{
+       int callno = (int)(long)(nothing);
+       struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_CONGESTION };
+       ast_pthread_mutex_lock(&iaxsl[callno]);
+       if (iaxs[callno]) {
+               iaxs[callno]->initid = -1;
+               iax_queue_frame(callno, &f);
+               ast_log(LOG_NOTICE, "Auto-congesting call due to slow response\n");
+       }
+       ast_pthread_mutex_unlock(&iaxsl[callno]);
+       return 0;
+}
+
 static int iax_call(struct ast_channel *c, char *dest, int timeout)
 {
        struct sockaddr_in sin;
@@ -1390,20 +1570,24 @@ static int iax_call(struct ast_channel *c, char *dest, int timeout)
        char context[AST_MAX_EXTENSION] ="";
        char *portno = NULL;
        struct chan_iax_pvt *p = c->pvt->pvt;
+       char *stringp=NULL;
        if ((c->_state != AST_STATE_DOWN) && (c->_state != AST_STATE_RESERVED)) {
                ast_log(LOG_WARNING, "Line is already in use (%s)?\n", c->name);
                return -1;
        }
        strncpy(host, dest, sizeof(host)-1);
-       strtok(host, "/");
+       stringp=host;
+       strsep(&stringp, "/");
        /* If no destination extension specified, use 's' */
-       rdest = strtok(NULL, "/");
+       rdest = strsep(&stringp, "/");
        if (!rdest) 
                rdest = myrdest;
-       strtok(rdest, "@");
-       rcontext = strtok(NULL, "@");
-       strtok(host, "@");
-       username = strtok(NULL, "@");
+       stringp=rdest;
+       strsep(&stringp, "@");
+       rcontext = strsep(&stringp, "@");
+       stringp=host;
+       strsep(&stringp, "@");
+       username = strsep(&stringp, "@");
        if (username) {
                /* Really the second argument is the host, not the username */
                hname = username;
@@ -1412,14 +1596,17 @@ static int iax_call(struct ast_channel *c, char *dest, int timeout)
                hname = host;
        }
        if (username) {
-               username = strtok(username, ":");
-               secret = strtok(NULL, ":");
+               stringp=username;
+               username = strsep(&stringp, ":");
+               secret = strsep(&stringp, ":");
        }
-       if (strtok(hname, ":")) {
-               strtok(hname, ":");
-               portno = strtok(hname, ":");
+       stringp=hname;
+       if (strsep(&stringp, ":")) {
+               stringp=hname;
+               strsep(&stringp, ":");
+               portno = strsep(&stringp, ":");
        }
-       if (create_addr(&sin, NULL, NULL, hname, context)) {
+       if (create_addr(&sin, NULL, NULL, NULL, hname, context)) {
                ast_log(LOG_WARNING, "No address associated with '%s'\n", hname);
                return -1;
        }
@@ -1464,59 +1651,38 @@ static int iax_call(struct ast_channel *c, char *dest, int timeout)
        /* Transmit the string in a "NEW" request */
        if (option_verbose > 2)
                ast_verbose(VERBOSE_PREFIX_3 "Calling using options '%s'\n", requeststr);
-       send_command((struct chan_iax_pvt *)c->pvt->pvt, AST_FRAME_IAX,
+       if (p->maxtime) {
+               /* Initialize pingtime and auto-congest time */
+               p->pingtime = p->maxtime / 2;
+               p->initid = ast_sched_add(sched, p->maxtime * 2, auto_congest, (void *)p->callno);
+       }
+       send_command(p, AST_FRAME_IAX,
                AST_IAX_COMMAND_NEW, 0, requeststr, strlen(requeststr) + 1, -1);
        ast_setstate(c, AST_STATE_RINGING);
        return 0;
 }
 
-static int iax_predestroy(struct chan_iax_pvt *pvt)
-{
-       struct ast_channel *c;
-       if (!pvt->alreadygone) {
-               /* 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);
-               pvt->pingid = -1;
-               pvt->lagid = -1;
-               pvt->autoid = -1;
-               pvt->alreadygone = 1;
-       }
-       c = pvt->owner;
-       if (c) {
-               c->_softhangup |= AST_SOFTHANGUP_DEV;
-               c->pvt->pvt = NULL;
-               pvt->owner = NULL;
-               ast_pthread_mutex_lock(&usecnt_lock);
-               usecnt--;
-               if (usecnt < 0) 
-                       ast_log(LOG_WARNING, "Usecnt < 0???\n");
-               ast_pthread_mutex_unlock(&usecnt_lock);
-               ast_update_use_count();
-       }
-       return 0;
-}
-
 static int iax_hangup(struct ast_channel *c) 
 {
        struct chan_iax_pvt *pvt = c->pvt->pvt;
        int alreadygone;
+       int callno;
        if (pvt) {
+               callno = pvt->callno;
+               ast_pthread_mutex_lock(&iaxsl[callno]);
                ast_log(LOG_DEBUG, "We're hanging up %s now...\n", c->name);
                alreadygone = pvt->alreadygone;
                /* Send the hangup unless we have had a transmission error or are already gone */
                if (!pvt->error && !alreadygone) 
                        send_command_final(pvt, AST_FRAME_IAX, AST_IAX_COMMAND_HANGUP, 0, NULL, 0, -1);
                /* Explicitly predestroy it */
-               iax_predestroy(pvt);
+               iax_predestroy_nolock(callno);
                /* If we were already gone to begin with, destroy us now */
                if (alreadygone) {
-                       iax_destroy(pvt->callno);
+                       ast_log(LOG_DEBUG, "Really destroying %s now...\n", c->name);
+                       iax_destroy_nolock(callno);
                }
+               ast_pthread_mutex_unlock(&iaxsl[callno]);
        }
        if (option_verbose > 2) 
                ast_verbose(VERBOSE_PREFIX_3 "Hungup '%s'\n", c->name);
@@ -1972,22 +2138,38 @@ static int iax_show_users(int fd, int argc, char *argv[])
 
 static int iax_show_peers(int fd, int argc, char *argv[])
 {
-#define FORMAT2 "%-15.15s  %-15.15s  %-15.15s %s  %-15.15s  %s\n"
-#define FORMAT "%-15.15s  %-15.15s  %-15.15s %s  %-15.15s  %d\n"
+#define FORMAT2 "%-15.15s  %-15.15s %s  %-15.15s  %-8s  %-10s\n"
+#define FORMAT "%-15.15s  %-15.15s %s  %-15.15s  %-8d  %-10s\n"
        struct iax_peer *peer;
+       char name[256] = "";
        if (argc != 3)
                return RESULT_SHOWUSAGE;
        ast_pthread_mutex_lock(&peerl.lock);
-       ast_cli(fd, FORMAT2, "Name", "Username", "Host", "   ", "Mask", "Port");
+       ast_cli(fd, FORMAT2, "Name/Username", "Host", "   ", "Mask", "Port", "Status");
        for (peer = peerl.peers;peer;peer = peer->next) {
                char nm[20];
+               char status[20];
+               if (strlen(peer->username))
+                       snprintf(name, sizeof(name), "%s/%s", peer->name, peer->username);
+               else
+                       strncpy(name, peer->name, sizeof(name) - 1);
+               if (peer->maxms) {
+                       if (peer->lastms < 0)
+                               strcpy(status, "UNREACHABLE");
+                       else if (peer->lastms > peer->maxms) 
+                               snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
+                       else if (peer->lastms) 
+                               snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
+                       else 
+                               strcpy(status, "UNKNOWN");
+               } else 
+                       strcpy(status, "Unmonitored");
                strncpy(nm, inet_ntoa(peer->mask), sizeof(nm)-1);
-               ast_cli(fd, FORMAT, peer->name, 
-                                       peer->username ? peer->username : "(Any)",
+               ast_cli(fd, FORMAT, name, 
                                        peer->addr.sin_addr.s_addr ? inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
-                                       peer->dynamic ? "(D)" : "   ",
+                                       peer->dynamic ? "(D)" : "(S)",
                                        nm,
-                                       ntohs(peer->addr.sin_port));
+                                       ntohs(peer->addr.sin_port), status);
        }
        ast_pthread_mutex_unlock(&peerl.lock);
        return RESULT_SUCCESS;
@@ -1995,6 +2177,16 @@ static int iax_show_peers(int fd, int argc, char *argv[])
 #undef FORMAT2
 }
 
+/* JDG: callback to display iax peers in manager */
+static int manager_iax_show_peers( struct mansession *s, struct message *m )
+{
+       char *a[] = { "iax", "show", "users" };
+       int ret;
+       ret = iax_show_peers( s->fd, 3, a );
+       ast_cli( s->fd, "\r\n" );
+       return ret;
+} /* /JDG */
+
 static char *regstate2str(int regstate)
 {
        switch(regstate) {
@@ -2050,9 +2242,9 @@ static int iax_show_channels(int fd, int argc, char *argv[])
        int x;
        if (argc != 3)
                return RESULT_SHOWUSAGE;
-       ast_pthread_mutex_lock(&iaxs_lock);
        ast_cli(fd, FORMAT2, "Peer", "Username", "ID (Lo/Rem)", "Seq (Tx/Rx)", "Lag", "Jitter", "Format");
-       for (x=0;x<AST_IAX_MAX_CALLS;x++)
+       for (x=0;x<AST_IAX_MAX_CALLS;x++) {
+               ast_pthread_mutex_lock(&iaxsl[x]);
                if (iaxs[x]) 
                        ast_cli(fd, FORMAT, inet_ntoa(iaxs[x]->addr.sin_addr), 
                                                strlen(iaxs[x]->username) ? iaxs[x]->username : "(None)", 
@@ -2061,7 +2253,8 @@ static int iax_show_channels(int fd, int argc, char *argv[])
                                                iaxs[x]->lag,
                                                iaxs[x]->jitter,
                                                iaxs[x]->voiceformat);
-       ast_pthread_mutex_unlock(&iaxs_lock);
+               ast_pthread_mutex_unlock(&iaxsl[x]);
+       }
        return RESULT_SUCCESS;
 #undef FORMAT
 #undef FORMAT2
@@ -2158,7 +2351,7 @@ static int __send_command(struct chan_iax_pvt *i, char type, int command, unsign
        f.frametype = type;
        f.subclass = command;
        f.datalen = datalen;
-       f.timelen = 0;
+       f.samples = 0;
        f.mallocd = 0;
        f.offset = 0;
        f.src = __FUNCTION__;
@@ -2180,7 +2373,8 @@ static int forward_command(struct chan_iax_pvt *i, char type, int command, unsig
 
 static int send_command_final(struct chan_iax_pvt *i, char type, int command, unsigned int ts, char *data, int datalen, int seqno)
 {
-       iax_predestroy(i);
+       /* It is assumed that the callno has already been locked */
+       iax_predestroy_nolock(i->callno);
        return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 1);
 }
 
@@ -2208,8 +2402,10 @@ static int iax_getformats(int callno, char *orequest)
 {
        char *var, *value;
        char request[256];
+       char *stringp=NULL;
        strncpy(request, orequest, sizeof(request)-1);
-       var = strtok(request, ";");
+       stringp=request;
+       var = strsep(&stringp, ";");
        while(var) {
                value = strchr(var, '=');
                if (value) {
@@ -2220,7 +2416,7 @@ static int iax_getformats(int callno, char *orequest)
                        } else 
                                ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
                }
-               var = strtok(NULL, ";");
+               var = strsep(&stringp, ";");
        }
        return 0;
 }
@@ -2235,10 +2431,12 @@ static int check_access(int callno, struct sockaddr_in *sin, char *orequest, int
        struct iax_user *user;
        char request[256];
        int gotcapability=0;
+       char *stringp=NULL;
        strncpy(request, orequest, sizeof(request)-1);
        if (!iaxs[callno])
                return res;
-       var = strtok(request, ";");
+       stringp=request;
+       var = strsep(&stringp, ";");
        while(var) {
                value = strchr(var, '=');
                if (value) { 
@@ -2270,7 +2468,7 @@ static int check_access(int callno, struct sockaddr_in *sin, char *orequest, int
                        else 
                                ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
                }
-               var = strtok(NULL, ";");
+               var = strsep(&stringp, ";");
        }
        if (!gotcapability) 
                iaxs[callno]->peercapability = iaxs[callno]->peerformat;
@@ -2366,11 +2564,13 @@ static int authenticate_verify(struct chan_iax_pvt *p, char *orequest)
        char rsasecret[256] = "";
        int res = -1; 
        int x;
+       char *stringp=NULL;
        
        if (!(p->state & IAX_STATE_AUTHENTICATED))
                return res;
        strncpy(request, orequest, sizeof(request)-1);
-       var = strtok(request, ";");
+       stringp=request;
+       var = strsep(&stringp, ";");
        while(var) {
                value = strchr(var, '=');
                if (value) { 
@@ -2385,14 +2585,16 @@ static int authenticate_verify(struct chan_iax_pvt *p, char *orequest)
                        else
                                ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
                }
-               var = strtok(NULL, ";");
+               var = strsep(&stringp, ";");
        }
        if (strstr(p->methods, "rsa") && strlen(rsasecret) && strlen(p->inkeys)) {
                struct ast_key *key;
                char *keyn;
                char tmpkey[256];
+               char *stringp=NULL;
                strncpy(tmpkey, p->inkeys, sizeof(tmpkey));
-               keyn = strtok(tmpkey, ":");
+               stringp=tmpkey;
+               keyn = strsep(&stringp, ":");
                while(keyn) {
                        key = ast_key_get(keyn, AST_KEY_PUBLIC);
                        if (key && !ast_check_signature(key, p->challenge, rsasecret)) {
@@ -2400,7 +2602,7 @@ static int authenticate_verify(struct chan_iax_pvt *p, char *orequest)
                                break;
                        } else if (!key)
                                ast_log(LOG_WARNING, "requested inkey '%s' for RSA authentication does not exist\n", keyn);
-                       keyn = strtok(NULL, ":");
+                       keyn = strsep(&stringp, ":");
                }
        } else if (strstr(p->methods, "md5")) {
                struct MD5Context md5;
@@ -2436,13 +2638,15 @@ static int register_verify(int callno, struct sockaddr_in *sin, char *orequest)
        char *keyn;
        int x;
        int expire = 0;
+       char *stringp=NULL;
 
        iaxs[callno]->state &= ~IAX_STATE_AUTHENTICATED;
        strcpy(iaxs[callno]->peer, "");
        if (!orequest)
                return -1;
        strncpy(request, orequest, sizeof(request)-1);
-       var = strtok(request, ";");
+       stringp=request;
+       var = strsep(&stringp, ";");
        while(var) {
                value = strchr(var, '=');
                if (value) { 
@@ -2461,7 +2665,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, char *orequest)
                        else 
                                ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
                }
-               var = strtok(NULL, ";");
+               var = strsep(&stringp, ";");
        }
 
        if (!strlen(peer)) {
@@ -2493,8 +2697,10 @@ static int register_verify(int callno, struct sockaddr_in *sin, char *orequest)
        if (strlen(rsasecret) && strstr(p->methods, "rsa") && strlen(p->challenge)) {
                if (strlen(p->inkeys)) {
                        char tmpkeys[256];
+                       char *stringp=NULL;
                        strncpy(tmpkeys, p->inkeys, sizeof(tmpkeys));
-                       keyn = strtok(tmpkeys, ":");
+                       stringp=tmpkeys;
+                       keyn = strsep(&stringp, ":");
                        while(keyn) {
                                key = ast_key_get(keyn, AST_KEY_PUBLIC);
                                if (key && !ast_check_signature(key, p->challenge, rsasecret)) {
@@ -2502,7 +2708,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, char *orequest)
                                        break;
                                } else if (!key) 
                                        ast_log(LOG_WARNING, "requested inkey '%s' does not exist\n", keyn);
-                               keyn = strtok(NULL, ":");
+                               keyn = strsep(&stringp, ":");
                        }
                        if (!keyn) {
                                ast_log(LOG_NOTICE, "Host %s failed RSA authentication with inkeys '%s'\n", peer, p->inkeys);
@@ -2605,9 +2811,11 @@ static int authenticate_reply(struct chan_iax_pvt *p, struct sockaddr_in *sin, c
        char methods[80] = "";
        char requeststr[256] = "";
        char *var, *value;
+       char *stringp=NULL;
        
        strncpy(request, orequest, sizeof(request)-1);
-       var = strtok(request, ";");
+       stringp=request;
+       var = strsep(&stringp, ";");
        while(var) {
                value = strchr(var, '=');
                if (value) { 
@@ -2622,7 +2830,7 @@ static int authenticate_reply(struct chan_iax_pvt *p, struct sockaddr_in *sin, c
                        else 
                                ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
                }
-               var = strtok(NULL, ";");
+               var = strsep(&stringp, ";");
        }
 
        /* Check for override RSA authentication first */
@@ -2674,12 +2882,14 @@ static int try_transfer(struct chan_iax_pvt *pvt, char *orequest)
        char *var, *value;
        
        struct sockaddr_in new;
+       char *stringp=NULL;
        
        if (!orequest)
                return -1;
 
        strncpy(request, orequest, sizeof(request)-1);
-       var = strtok(request, ";");
+       stringp=request;
+       var = strsep(&stringp, ";");
        while(var) {
                value = strchr(var, '=');
                if (value) { 
@@ -2694,7 +2904,7 @@ static int try_transfer(struct chan_iax_pvt *pvt, char *orequest)
                        else 
                                ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
                }
-               var = strtok(NULL, ";");
+               var = strsep(&stringp, ";");
        }
        if (!newcall || !inet_aton(newip, &new.sin_addr) || !newport) {
                ast_log(LOG_WARNING, "Invalid transfer request\n");
@@ -2717,10 +2927,13 @@ static int complete_dpreply(struct chan_iax_pvt *pvt, char *orequest)
        int status = CACHE_FLAG_UNKNOWN;
        int expirey = iaxdefaultdpcache;
        int x;
+       int matchmore = 0;
        struct iax_dpcache *dp, *prev;
+       char *stringp=NULL;
        
        strncpy(request, orequest, sizeof(request)-1);
-       var = strtok(request, ";");
+       stringp=request;
+       var = strsep(&stringp, ";");
        while(var) {
                value = strchr(var, '=');
                if (value) { 
@@ -2741,10 +2954,12 @@ static int complete_dpreply(struct chan_iax_pvt *pvt, char *orequest)
                                expirey = atoi(value);
                        else if (!strcmp(var, "ignorepat")) {
                                /* Don' really do much with it */
-                       } else 
+                       } else if (!strcmp(var, "matchmore")) {
+                               matchmore = CACHE_FLAG_MATCHMORE;
+                       } else
                                ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
                }
-               var = strtok(NULL, ";");
+               var = strsep(&stringp, ";");
        }
        ast_pthread_mutex_lock(&dpcache_lock);
        prev = NULL;
@@ -2762,6 +2977,7 @@ static int complete_dpreply(struct chan_iax_pvt *pvt, char *orequest)
                        if (dp->flags & CACHE_FLAG_PENDING) {
                                dp->flags &= ~CACHE_FLAG_PENDING;
                                dp->flags |= status;
+                               dp->flags |= CACHE_FLAG_MATCHMORE;
                        }
                        /* Wake up waiters */
                        for (x=0;x<sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++)
@@ -2782,11 +2998,13 @@ static int complete_transfer(int callno, char *orequest)
        char *var, *value;
        struct chan_iax_pvt *pvt = iaxs[callno];
        struct ast_iax_frame *cur;
+       char *stringp=NULL;
        if (!orequest)
                return -1;
 
        strncpy(request, orequest, sizeof(request)-1);
-       var = strtok(request, ";");
+       stringp=request;
+       var = strsep(&stringp, ";");
        while(var) {
                value = strchr(var, '=');
                if (value) { 
@@ -2797,7 +3015,7 @@ static int complete_transfer(int callno, char *orequest)
                        else 
                                ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
                }
-               var = strtok(NULL, ";");
+               var = strsep(&stringp, ";");
        }
        if (peercallno < 0) {
                ast_log(LOG_WARNING, "Invalid transfer request\n");
@@ -2835,7 +3053,7 @@ static int complete_transfer(int callno, char *orequest)
        return 0; 
 }
 
-static int iax_ack_registry(char *orequest, struct sockaddr_in *sin)
+static int iax_ack_registry(char *orequest, struct sockaddr_in *sin, int callno)
 {
        struct iax_registry *reg;
        /* Start pessimistic */
@@ -2846,12 +3064,14 @@ static int iax_ack_registry(char *orequest, struct sockaddr_in *sin)
        char ourip[256] = "<Unspecified>";
        struct sockaddr_in oldus;
        char *var, *value;
+       char *stringp=NULL;
 
        if (!orequest)
                return -1;
 
        strncpy(request, orequest, sizeof(request)-1);
-       var = strtok(request, ";");
+       stringp=request;
+       var = strsep(&stringp, ";");
        while(var) {
                value = strchr(var, '=');
                if (value) { 
@@ -2870,11 +3090,9 @@ static int iax_ack_registry(char *orequest, struct sockaddr_in *sin)
                        } else 
                                ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
                }
-               var = strtok(NULL, ";");
+               var = strsep(&stringp, ";");
        }
-       reg = registrations;
-       while(reg) {
-               if (!strcasecmp(reg->username, peer)) {
+       reg = iaxs[callno]->reg;
                        memcpy(&oldus, &reg->us, sizeof(oldus));
                        if (memcmp(&reg->addr, sin, sizeof(&reg->addr))) {
                                ast_log(LOG_WARNING, "Received unsolicited registry ack from '%s'\n", inet_ntoa(sin->sin_addr));
@@ -2898,9 +3116,6 @@ static int iax_ack_registry(char *orequest, struct sockaddr_in *sin)
                        }
                        reg->regstate = REG_STATE_REGISTERED;
                        return 0;
-               }
-               reg = reg->next;
-       }
        ast_log(LOG_WARNING, "Registry acknowledge on unknown registery '%s'\n", peer);
        return -1;
 }
@@ -2911,21 +3126,25 @@ static int iax_register(char *value, int lineno)
        char copy[256];
        char *username, *hostname, *secret;
        char *porta;
+       char *stringp=NULL;
        
        struct hostent *hp;
        if (!value)
                return -1;
        strncpy(copy, value, sizeof(copy)-1);
-       username = strtok(copy, "@");
-       hostname = strtok(NULL, "@");
+       stringp=copy;
+       username = strsep(&stringp, "@");
+       hostname = strsep(&stringp, "@");
        if (!hostname) {
                ast_log(LOG_WARNING, "Format for registration is user[:secret]@host[:port] at line %d", lineno);
                return -1;
        }
-       username = strtok(username, ":");
-       secret = strtok(NULL, ":");
-       hostname = strtok(hostname, ":");
-       porta = strtok(NULL, ";");
+       stringp=username;
+       username = strsep(&stringp, ":");
+       secret = strsep(&stringp, ":");
+       stringp=hostname;
+       hostname = strsep(&stringp, ":");
+       porta = strsep(&stringp, ":");
        
        if (porta && !atoi(porta)) {
                ast_log(LOG_WARNING, "%s is not a valid port number at line %d\n", porta, lineno);
@@ -2971,8 +3190,13 @@ static int expire_registry(void *data)
        return 0;
 }
 
+
+static int iax_poke_peer(struct iax_peer *peer);
+
+
 static int update_registry(char *name, struct sockaddr_in *sin, int callno)
 {
+       /* Called from IAX thread only, with proper iaxsl lock */
        char requeststr[256] = "";
        struct iax_peer *p;
        for (p = peerl.peers;p;p = p->next) {
@@ -2983,6 +3207,7 @@ static int update_registry(char *name, struct sockaddr_in *sin, int callno)
                                if  (option_verbose > 2)
                                ast_verbose(VERBOSE_PREFIX_3 "Registered '%s' (%s) at %s:%d\n", p->name, 
                                        iaxs[callno]->state & IAX_STATE_AUTHENTICATED ? "AUTHENTICATED" : "UNAUTHENTICATED", inet_ntoa(sin->sin_addr), htons(sin->sin_port));
+                               iax_poke_peer(p);
                        }               
                        /* Update the host */
                        memcpy(&p->addr, sin, sizeof(p->addr));
@@ -3034,12 +3259,14 @@ static int registry_rerequest(char *orequest, int callno, struct sockaddr_in *si
        char challenge[256] = "";
        char *var, *value;
        int res;
+       char *stringp=NULL;
 
        if (!orequest)
                return -1;
 
        strncpy(request, orequest, sizeof(request)-1);
-       var = strtok(request, ";");
+       stringp=request;
+       var = strsep(&stringp, ";");
        while(var) {
                value = strchr(var, '=');
                if (value) { 
@@ -3054,11 +3281,9 @@ static int registry_rerequest(char *orequest, int callno, struct sockaddr_in *si
                        else 
                                ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
                }
-               var = strtok(NULL, ";");
+               var = strsep(&stringp, ";");
        }
-       reg = registrations;
-       while(reg) {
-               if (!strcasecmp(reg->username, peer)) {
+       reg = iaxs[callno]->reg;
                        if (memcmp(&reg->addr, sin, sizeof(&reg->addr))) {
                                ast_log(LOG_WARNING, "Received unsolicited registry authenticate request from '%s'\n", inet_ntoa(sin->sin_addr));
                                return -1;
@@ -3081,9 +3306,6 @@ static int registry_rerequest(char *orequest, int callno, struct sockaddr_in *si
                                return send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_REGREQ, 0, requeststr, strlen(requeststr) + 1, -1);
                        } else
                                return -1;
-               }
-               reg = reg->next;
-       }
        ast_log(LOG_WARNING, "Registry acknowledge on unknown registery '%s'\n", peer);
        return -1;
 }
@@ -3099,16 +3321,22 @@ static int stop_stuff(int callno)
                if (iaxs[callno]->autoid > -1)
                        ast_sched_del(sched, iaxs[callno]->autoid);
                iaxs[callno]->autoid = -1;
+               if (iaxs[callno]->initid > -1)
+                       ast_sched_del(sched, iaxs[callno]->initid);
+               iaxs[callno]->initid = -1;
                return 0;
 }
 
 static int auto_hangup(void *nothing)
 {
+       /* Called from IAX thread only, without iaxs lock */
        int callno = (int)(long)(nothing);
+       ast_pthread_mutex_lock(&iaxsl[callno]);
        if (iaxs[callno]) {
                iaxs[callno]->autoid = -1;
                send_command_final(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_HANGUP, 0, "Timeout", strlen("Timeout") + 1, -1);
        }
+       ast_pthread_mutex_unlock(&iaxsl[callno]);
        return 0;
 }
 
@@ -3143,6 +3371,14 @@ static void vnak_retransmit(int callno, int last)
        ast_pthread_mutex_unlock(&iaxq.lock);
 }
 
+static int iax_poke_peer_s(void *data)
+{
+       struct iax_peer *peer = data;
+       peer->pokeexpire = -1;
+       iax_poke_peer(peer);
+       return 0;
+}
+
 static int socket_read(int *id, int fd, short events, void *cbdata)
 {
        struct sockaddr_in sin;
@@ -3158,8 +3394,10 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
        struct ast_frame f;
        struct ast_channel *c;
        struct iax_dpcache *dp;
+       struct iax_peer *peer;
        int format;
        int exists;
+       int mm;
        char rel0[256];
        char rel1[255];
        char empty[32]="";              /* Safety measure */
@@ -3188,15 +3426,20 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                f.subclass = fh->subclasshigh << 16;
                f.subclass += ntohs(fh->subclasslow);
 #endif
-               if ((f.frametype == AST_FRAME_IAX) && ((f.subclass == AST_IAX_COMMAND_NEW) || (f.subclass == AST_IAX_COMMAND_REGREQ)))
+               if ((f.frametype == AST_FRAME_IAX) && ((f.subclass == AST_IAX_COMMAND_NEW) || (f.subclass == AST_IAX_COMMAND_REGREQ)
+                               || (f.subclass == AST_IAX_COMMAND_POKE)))
                        new = NEW_ALLOW;
        } else {
                /* Don't knwo anything about it yet */
                f.frametype = AST_FRAME_NULL;
                f.subclass = 0;
        }
-       ast_pthread_mutex_lock(&iaxs_lock);
+
        fr.callno = find_callno(ntohs(mh->callno) & ~AST_FLAG_FULL, dcallno, &sin, new);
+
+       if (fr.callno > 0) 
+               ast_pthread_mutex_lock(&iaxsl[fr.callno]);
+
        if ((fr.callno < 0) || !iaxs[fr.callno]) {
                /* A call arrived for a non-existant destination.  Unless it's an "inval"
                   frame, reply with an inval */
@@ -3209,7 +3452,8 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                raw_hangup(&sin, ntohs(fh->dcallno), ntohs(mh->callno) & ~AST_FLAG_FULL
                                );
                }
-               ast_pthread_mutex_unlock(&iaxs_lock);
+               if (fr.callno > 0) 
+                       ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
                return 1;
        }
        if (((f.subclass != AST_IAX_COMMAND_TXCNT) &&
@@ -3248,7 +3492,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                        /* Send a VNAK requesting retransmission */
                                        iax_vnak(fr.callno);
                                }
-                               ast_pthread_mutex_unlock(&iaxs_lock);
+                               ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
                                return 1;
                        }
                } else {
@@ -3264,7 +3508,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                /* A full frame */
                if (res < sizeof(struct ast_iax_full_hdr)) {
                        ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, sizeof(struct ast_iax_full_hdr));
-                       ast_pthread_mutex_unlock(&iaxs_lock);
+                       ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
                        return 1;
                }
                f.datalen = res - sizeof(struct ast_iax_full_hdr);
@@ -3298,6 +3542,11 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                        }
                }
                if (f.frametype == AST_FRAME_IAX) {
+                       if (iaxs[fr.callno]->initid > -1) {
+                               /* Don't auto congest anymore since we've gotten something usefulb ack */
+                               ast_sched_del(sched, iaxs[fr.callno]->initid);
+                               iaxs[fr.callno]->initid = -1;
+                       }
                        /* Handle the IAX pseudo frame itself */
                        if (option_debug)
                                ast_log(LOG_DEBUG, "IAX subclass %d received\n", f.subclass);
@@ -3314,8 +3563,11 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                        if ((fr.callno == cur->callno) && (fr.seqno == cur->seqno)) {
                                                cur->retries = -1;
                                                /* Destroy call if this is the end */
-                                               if (cur->final) 
-                                                       iax_destroy(fr.callno);
+                                               if (cur->final) { 
+                                                       if (option_debug)
+                                                               ast_log(LOG_DEBUG, "Really destroying %d, having been acked on final message\n", fr.callno);
+                                                       iax_destroy_nolock(fr.callno);
+                                               }
                                        }
                                }
                                ast_pthread_mutex_unlock(&iaxq.lock);
@@ -3354,10 +3606,8 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                        ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s'\n", inet_ntoa(sin.sin_addr), (char *)f.data);
                                        break;
                                }
-                               ast_pthread_mutex_unlock(&iaxs_lock);
                                /* This might re-enter the IAX code and need the lock */
                                exists = ast_exists_extension(NULL, iaxs[fr.callno]->context, iaxs[fr.callno]->exten, 1, iaxs[fr.callno]->callerid);
-                               ast_pthread_mutex_lock(&iaxs_lock);
                                if (!strlen(iaxs[fr.callno]->secret) && !strlen(iaxs[fr.callno]->inkeys)) {
                                        if (strcmp(iaxs[fr.callno]->exten, "TBD") && !exists) {
                                                send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No such context/extension", strlen("No such context/extension"), -1);
@@ -3382,23 +3632,23 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                                                }
                                                        }
                                                }
-                                               /* No authentication required, let them in */
-                                               snprintf(rel1, sizeof(rel1), "formats=%d;", format);
-                                               send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACCEPT, 0, rel1, strlen(rel1) + 1, -1);
-                                               if (strcmp(iaxs[fr.callno]->exten, "TBD")) {
-                                                       iaxs[fr.callno]->state |= IAX_STATE_STARTED;
-                                                       if (option_verbose > 2) 
-                                                               ast_verbose(VERBOSE_PREFIX_3 "Accepting unauthenticated call from %s, requested format = %d, actual format = %d\n", 
-                                                                       inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformat,format);
-                                                       if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING, iax_capability))) {
-                                                               iax_destroy(fr.callno);
-                                                       } else
-                                                               c->nativeformats = format;
-                                               } else {
-                                                       iaxs[fr.callno]->state |= IAX_STATE_TBD;
-                                                       /* If this is a TBD call, we're ready but now what...  */
-                                                       if (option_verbose > 2)
-                                                               ast_verbose(VERBOSE_PREFIX_3 "Accepted unauthenticated TBD call from %s\n", inet_ntoa(sin.sin_addr));
+                                               if (format) {
+                                                       /* No authentication required, let them in */
+                                                       snprintf(rel1, sizeof(rel1), "formats=%d;", format);
+                                                       send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACCEPT, 0, rel1, strlen(rel1) + 1, -1);
+                                                       if (strcmp(iaxs[fr.callno]->exten, "TBD")) {
+                                                               iaxs[fr.callno]->state |= IAX_STATE_STARTED;
+                                                               if (option_verbose > 2) 
+                                                                       ast_verbose(VERBOSE_PREFIX_3 "Accepting unauthenticated call from %s, requested format = %d, actual format = %d\n", 
+                                                                               inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformat,format);
+                                                               if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING, format)))
+                                                                       iax_destroy_nolock(fr.callno);
+                                                       } else {
+                                                               iaxs[fr.callno]->state |= IAX_STATE_TBD;
+                                                               /* If this is a TBD call, we're ready but now what...  */
+                                                               if (option_verbose > 2)
+                                                                       ast_verbose(VERBOSE_PREFIX_3 "Accepted unauthenticated TBD call from %s\n", inet_ntoa(sin.sin_addr));
+                                                       }
                                                }
                                        }
                                        break;
@@ -3411,6 +3661,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                ((char *)f.data)[f.datalen] = '\0';
                                if ((iaxs[fr.callno]->state & IAX_STATE_TBD) && 
                                        !(iaxs[fr.callno]->state & IAX_STATE_STARTED) && f.datalen) {
+                                       mm = ast_matchmore_extension(NULL, iaxs[fr.callno]->context, (char *)f.data, 1, iaxs[fr.callno]->callerid);
                                        /* Must be started */
                                        if (ast_exists_extension(NULL, iaxs[fr.callno]->context, (char *)f.data, 1, iaxs[fr.callno]->callerid)) {
                                                strcpy(rel0, "exists");
@@ -3419,16 +3670,17 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                        } else {
                                                strcpy(rel0, "nonexistant");
                                        }
-                                       snprintf(rel1, sizeof(rel1), "number=%s;status=%s;ignorepat=%s;expirey=%d;",
+                                       snprintf(rel1, sizeof(rel1), "number=%s;status=%s;ignorepat=%s;expirey=%d;matchmore=%s;",
                                                (char *)f.data, rel0,
                                                ast_ignore_pattern(iaxs[fr.callno]->context, (char *)f.data) ? "yes" : "no",
-                                               iaxdefaultdpcache);
+                                               iaxdefaultdpcache, mm ? "yes" : "no");
                                        send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_DPREP, 0, rel1, strlen(rel1) + 1, -1);
                                }
                                break;
                        case AST_IAX_COMMAND_HANGUP:
                                iaxs[fr.callno]->alreadygone = 1;
-                               iax_destroy(fr.callno);
+                               ast_log(LOG_DEBUG, "Immediately destroying %d, having received hangup\n", fr.callno);
+                               iax_destroy_nolock(fr.callno);
                                break;
                        case AST_IAX_COMMAND_REJECT:
                                if (f.data)
@@ -3436,7 +3688,8 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                if (iaxs[fr.callno]->owner)
                                        ast_log(LOG_WARNING, "Call rejected by %s: %s\n", inet_ntoa(iaxs[fr.callno]->addr.sin_addr), (char *)f.data);
                                iaxs[fr.callno]->error = EPERM;
-                               iax_destroy(fr.callno);
+                               ast_log(LOG_DEBUG, "Immediately destroying %d, having received reject\n", fr.callno);
+                               iax_destroy_nolock(fr.callno);
                                break;
                        case AST_IAX_COMMAND_ACCEPT:
                                /* Ignore if call is already up or needs authentication or is a TBD */
@@ -3480,6 +3733,10 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                }
                                ast_pthread_mutex_unlock(&dpcache_lock);
                                break;
+                       case AST_IAX_COMMAND_POKE:
+                               /* Send back a pong packet with the original timestamp */
+                               send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_PONG, fr.ts, NULL, 0, -1);
+                               break;
                        case AST_IAX_COMMAND_PING:
 #ifdef BRIDGE_OPTIMIZATION
                                if (iaxs[fr.callno]->bridgecallno > -1) {
@@ -3492,7 +3749,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
 #else                          
                                /* Send back a pong packet with the original timestamp */
                                send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_PONG, fr.ts, NULL, 0, -1);
-#endif                         
+#endif                 
                                break;
                        case AST_IAX_COMMAND_PONG:
 #ifdef BRIDGE_OPTIMIZATION
@@ -3506,7 +3763,27 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
 #else
                                /* Calculate ping time */
                                iaxs[fr.callno]->pingtime =  calc_timestamp(iaxs[fr.callno], 0) - fr.ts;
-#endif                                 
+#endif
+                               if (iaxs[fr.callno]->peerpoke) {
+                                       peer = iaxs[fr.callno]->peerpoke;
+                                       if ((peer->lastms < 0)  || (peer->lastms > peer->maxms)) {
+                                               if (iaxs[fr.callno]->pingtime <= peer->maxms)
+                                                       ast_log(LOG_NOTICE, "Peer '%s' is now REACHABLE!\n", peer->name);
+                                       } else if ((peer->lastms > 0) && (peer->lastms <= peer->maxms)) {
+                                               if (iaxs[fr.callno]->pingtime > peer->maxms)
+                                                       ast_log(LOG_NOTICE, "Peer '%s' is now TOO LAGGED (%d ms)!\n", peer->name, iaxs[fr.callno]->pingtime);
+                                       }
+                                       peer->lastms = iaxs[fr.callno]->pingtime;
+                                       peer->callno = -1;
+                                       if (peer->pokeexpire > -1)
+                                               ast_sched_del(sched, peer->pokeexpire);
+                                       iax_destroy_nolock(fr.callno);
+                                       /* Try again eventually */
+                                       if ((peer->lastms < 0)  || (peer->lastms > peer->maxms))
+                                               peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, iax_poke_peer_s, peer);
+                                       else
+                                               peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_OK, iax_poke_peer_s, peer);
+                               }
                                break;
                        case AST_IAX_COMMAND_LAGRQ:
                        case AST_IAX_COMMAND_LAGRP:
@@ -3523,7 +3800,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                        f.mallocd = 0;
                                        f.offset = 0;
                                        fr.f = &f;
-                                       f.timelen = 0;
+                                       f.samples = 0;
                                        schedule_delivery(iaxfrdup2(&fr, 0), 1);
 #ifdef BRIDGE_OPTIMIZATION
                                }
@@ -3553,13 +3830,11 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                        send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No authority found", strlen("No authority found"), -1);
                                        break;
                                }
-                               ast_pthread_mutex_unlock(&iaxs_lock);
                                /* This might re-enter the IAX code and need the lock */
                                exists = ast_exists_extension(NULL, iaxs[fr.callno]->context, iaxs[fr.callno]->exten, 1, iaxs[fr.callno]->callerid);
-                               ast_pthread_mutex_lock(&iaxs_lock);
                                if (strcmp(iaxs[fr.callno]->exten, "TBD") && !exists) {
-                                       send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No such context/extension", strlen("No such context/extension"), -1);
                                        ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n", inet_ntoa(sin.sin_addr), iaxs[fr.callno]->exten, iaxs[fr.callno]->context);
+                                       send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No such context/extension", strlen("No such context/extension"), -1);
                                } else {
                                        /* Select an appropriate format */
                                        format = iaxs[fr.callno]->peerformat & iax_capability;
@@ -3567,36 +3842,36 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                                ast_log(LOG_DEBUG, "We don't do requested format %d, falling back to peer capability %d\n", iaxs[fr.callno]->peerformat, iaxs[fr.callno]->peercapability);
                                                format = iaxs[fr.callno]->peercapability & iax_capability;
                                                if (!format) {
-                                                       send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "Unable to negotiate codec", strlen("Unable to negotiate codec"), -1);
                                                        ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible  with our capability 0x%x.\n", inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformat, iaxs[fr.callno]->peercapability, iax_capability);
+                                                       send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "Unable to negotiate codec", strlen("Unable to negotiate codec"), -1);
                                                } else {
                                                        /* Pick one... */
                                                        format = ast_best_codec(iaxs[fr.callno]->peercapability & iax_capability);
                                                        if (!format) {
                                                                ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[fr.callno]->peercapability & iax_capability);
-                                                               send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "Unable to negotiate codec", strlen("Unable to negotiate codec"), -1);
                                                                ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible  with our capability 0x%x.\n", inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformat, iaxs[fr.callno]->peercapability, iax_capability);
+                                                               send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "Unable to negotiate codec", strlen("Unable to negotiate codec"), -1);
                                                        }
                                                }
                                        }
-                                       /* Authentication received */
-                                       snprintf(rel1, sizeof(rel1), "formats=%d;", format);
-                                       send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACCEPT, 0, rel1, strlen(rel1) + 1, -1);
-                                       if (strcmp(iaxs[fr.callno]->exten, "TBD")) {
-                                               iaxs[fr.callno]->state |= IAX_STATE_STARTED;
-                                               if (option_verbose > 2) 
-                                                       ast_verbose(VERBOSE_PREFIX_3 "Accepting AUTHENTICATED call from %s, requested format = %d, actual format = %d\n", 
-                                                               inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformat,format);
-                                               iaxs[fr.callno]->state |= IAX_STATE_STARTED;
-                                               if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING, iax_capability))) {
-                                                       iax_destroy(fr.callno);
-                                               } else
-                                                       c->nativeformats = iaxs[fr.callno]->peerformat;
-                                       } else {
-                                               iaxs[fr.callno]->state |= IAX_STATE_TBD;
-                                               /* If this is a TBD call, we're ready but now what...  */
-                                               if (option_verbose > 2)
-                                                       ast_verbose(VERBOSE_PREFIX_3 "Accepted AUTHENTICATED TBD call from %s\n", inet_ntoa(sin.sin_addr));
+                                       if (format) {
+                                               /* Authentication received */
+                                               snprintf(rel1, sizeof(rel1), "formats=%d;", format);
+                                               send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACCEPT, 0, rel1, strlen(rel1) + 1, -1);
+                                               if (strcmp(iaxs[fr.callno]->exten, "TBD")) {
+                                                       iaxs[fr.callno]->state |= IAX_STATE_STARTED;
+                                                       if (option_verbose > 2) 
+                                                               ast_verbose(VERBOSE_PREFIX_3 "Accepting AUTHENTICATED call from %s, requested format = %d, actual format = %d\n", 
+                                                                       inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformat,format);
+                                                       iaxs[fr.callno]->state |= IAX_STATE_STARTED;
+                                                       if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING, format)))
+                                                               iax_destroy_nolock(fr.callno);
+                                               } else {
+                                                       iaxs[fr.callno]->state |= IAX_STATE_TBD;
+                                                       /* If this is a TBD call, we're ready but now what...  */
+                                                       if (option_verbose > 2)
+                                                               ast_verbose(VERBOSE_PREFIX_3 "Accepted AUTHENTICATED TBD call from %s\n", inet_ntoa(sin.sin_addr));
+                                               }
                                        }
                                }
                                break;
@@ -3606,23 +3881,22 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                        iaxs[fr.callno]->state &= ~IAX_STATE_TBD;
                                        strncpy(iaxs[fr.callno]->exten, (char *)f.data, sizeof(iaxs[fr.callno]->exten)-1);      
                                        if (!ast_exists_extension(NULL, iaxs[fr.callno]->context, iaxs[fr.callno]->exten, 1, iaxs[fr.callno]->callerid)) {
-                                               send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No such context/extension", strlen("No such context/extension"), -1);
                                                ast_log(LOG_NOTICE, "Rejected dial attempt from %s, request '%s@%s' does not exist\n", inet_ntoa(sin.sin_addr), iaxs[fr.callno]->exten, iaxs[fr.callno]->context);
+                                               send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No such context/extension", strlen("No such context/extension"), -1);
                                        } else {
                                                iaxs[fr.callno]->state |= IAX_STATE_STARTED;
                                                if (option_verbose > 2) 
                                                        ast_verbose(VERBOSE_PREFIX_3 "Accepting DIAL from %s, formats = 0x%x\n", inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformat);
                                                iaxs[fr.callno]->state |= IAX_STATE_STARTED;
-                                               if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING, iax_capability))) {
-                                                       iax_destroy(fr.callno);
-                                               } else
-                                                       c->nativeformats = iaxs[fr.callno]->peerformat;
+                                               if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING, iaxs[fr.callno]->peerformat)))
+                                                       iax_destroy_nolock(fr.callno);
                                        }
                                }
                                break;
                        case AST_IAX_COMMAND_INVAL:
                                iaxs[fr.callno]->error = ENOTCONN;
-                               iax_destroy(fr.callno);
+                               ast_log(LOG_DEBUG, "Immediately destroying %d, having received INVAL\n", fr.callno);
+                               iax_destroy_nolock(fr.callno);
                                if (option_debug)
                                        ast_log(LOG_DEBUG, "Destroying call %d\n", fr.callno);
                                break;
@@ -3648,9 +3922,9 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                        case AST_IAX_COMMAND_REGACK:
                                if (f.data)
                                        ((char *)f.data)[f.datalen] = '\0';
-                               if (iax_ack_registry(f.data, &sin)) 
+                               if (iax_ack_registry(f.data, &sin, fr.callno)) 
                                        ast_log(LOG_WARNING, "Registration failure\n");
-                               iax_destroy(fr.callno);
+                               iax_destroy_nolock(fr.callno);
                                break;
                        case AST_IAX_COMMAND_REGREJ:
                                if (f.data)
@@ -3659,7 +3933,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                        ast_log(LOG_NOTICE, "Registration of '%s' rejected: %s\n", iaxs[fr.callno]->reg->username, (char *)f.data);
                                        iaxs[fr.callno]->reg->regstate = REG_STATE_REJECTED;
                                }
-                               iax_destroy(fr.callno);
+                               iax_destroy_nolock(fr.callno);
                                break;
                        case AST_IAX_COMMAND_REGAUTH:
                                /* Authentication request */
@@ -3734,7 +4008,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                ast_log(LOG_DEBUG, "Unknown IAX command %d on %d/%d\n", f.subclass, fr.callno, iaxs[fr.callno]->peercallno);
                        }
                        /* Don't actually pass these frames along */
-                       ast_pthread_mutex_unlock(&iaxs_lock);
+                       ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
                        return 1;
                }
        } else {
@@ -3745,13 +4019,13 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                else {
                        ast_log(LOG_WARNING, "Received mini frame before first full voice frame\n ");
                        iax_vnak(fr.callno);
-                       ast_pthread_mutex_unlock(&iaxs_lock);
+                       ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
                        return 1;
                }
                f.datalen = res - sizeof(struct ast_iax_mini_hdr);
                if (f.datalen < 0) {
                        ast_log(LOG_WARNING, "Datalen < 0?\n");
-                       ast_pthread_mutex_unlock(&iaxs_lock);
+                       ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
                        return 1;
                }
                if (f.datalen)
@@ -3762,7 +4036,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
        }
        /* Don't pass any packets until we're started */
        if (!(iaxs[fr.callno]->state & IAX_STATE_STARTED)) {
-               ast_pthread_mutex_unlock(&iaxs_lock);
+               ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
                return 1;
        }
        /* Common things */
@@ -3771,9 +4045,9 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
        f.offset = 0;
        fr.f = &f;
        if (f.datalen && (f.frametype == AST_FRAME_VOICE)) 
-               f.timelen = get_timelen(&f);
+               f.samples = get_samples(&f);
        else
-               f.timelen = 0;
+               f.samples = 0;
 
        /* If this is our most recent packet, use it as our basis for timestamping */
        if (iaxs[fr.callno]->last < fr.ts) {
@@ -3794,7 +4068,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
        schedule_delivery(iaxfrdup2(&fr, 0), 1);
 #endif
        /* Always run again */
-       ast_pthread_mutex_unlock(&iaxs_lock);
+       ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
        return 1;
 }
 
@@ -3826,6 +4100,51 @@ static int iax_do_register(struct iax_registry *reg)
        return 0;
 }
 
+
+static int iax_poke_noanswer(void *data)
+{
+       struct iax_peer *peer = data;
+       peer->pokeexpire = -1;
+       if (peer->lastms > -1)
+               ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE!\n", peer->name);
+       if (peer->callno > 0)
+               iax_destroy(peer->callno);
+       peer->callno = 0;
+       peer->lastms = -1;
+       /* Try again quickly */
+       peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, iax_poke_peer_s, peer);
+       return 0;
+}
+
+static int iax_poke_peer(struct iax_peer *peer)
+{
+       if (!peer->maxms || !peer->addr.sin_addr.s_addr) {
+               /* IF we have no IP, or this isn't to be monitored, return
+                 imeediately after clearing things out */
+               peer->lastms = 0;
+               peer->pokeexpire = -1;
+               peer->callno = 0;
+               return 0;
+       }
+       if (peer->callno > 0) {
+               ast_log(LOG_NOTICE, "Still have a callno...\n");
+               iax_destroy(peer->callno);
+       }
+       peer->callno = find_callno(-1, -1, &peer->addr, NEW_FORCE);
+       if (peer->callno < 0) {
+               ast_log(LOG_WARNING, "Unable to allocate call for poking peer '%s'\n", peer->name);
+               return -1;
+       }
+       if (peer->pokeexpire > -1)
+               ast_sched_del(sched, peer->pokeexpire);
+       /* Speed up retransmission times */
+       iaxs[peer->callno]->pingtime = peer->maxms / 4 + 1;
+       iaxs[peer->callno]->peerpoke = peer;
+       send_command(iaxs[peer->callno], AST_FRAME_IAX, AST_IAX_COMMAND_POKE, 0, NULL, 0, -1);
+       peer->pokeexpire = ast_sched_add(sched, DEFAULT_MAXMS * 2, iax_poke_noanswer, peer);
+       return 0;
+}
+
 static void free_context(struct iax_context *con)
 {
        struct iax_context *conl;
@@ -3841,31 +4160,39 @@ static struct ast_channel *iax_request(char *type, int format, void *data)
        int callno;
        int res;
        int sendani;
+       int maxtime;
        int fmt, native;
        struct sockaddr_in sin;
        char s[256];
        char *st;
        struct ast_channel *c;
+       char *stringp=NULL;
        int capability = iax_capability;
        strncpy(s, (char *)data, sizeof(s)-1);
-       strtok(s, "/");
-       strtok(s, "@");
-       st = strtok(NULL, "@");
+       /* FIXME The next two lines seem useless */
+       stringp=s;
+       strsep(&stringp, "/");
+
+       stringp=s;
+       strsep(&stringp, "@");
+       st = strsep(&stringp, "@");
        if (!st)
                st = s;
        /* Populate our address from the given */
-       if (create_addr(&sin, &capability, &sendani, st, NULL)) {
+       if (create_addr(&sin, &capability, &sendani, &maxtime, st, NULL)) {
                return NULL;
        }
-       ast_pthread_mutex_lock(&iaxs_lock);
        callno = find_callno(-1, -1, &sin, NEW_FORCE);
        if (callno < 0) {
                ast_log(LOG_WARNING, "Unable to create call\n");
                return NULL;
        }
+       ast_pthread_mutex_lock(&iaxsl[callno]);
        /* Keep track of sendani flag */
        iaxs[callno]->sendani = sendani;
+       iaxs[callno]->maxtime = maxtime;
        c = ast_iax_new(iaxs[callno], AST_STATE_DOWN, capability);
+       ast_pthread_mutex_unlock(&iaxsl[callno]);
        if (c) {
                /* Choose a format we can live with */
                if (c->nativeformats & format)
@@ -3877,13 +4204,11 @@ static struct ast_channel *iax_request(char *type, int format, void *data)
                        if (res < 0) {
                                ast_log(LOG_WARNING, "Unable to create translator path for %d to %d on %s\n", c->nativeformats, fmt, c->name);
                                ast_hangup(c);
-                               ast_pthread_mutex_unlock(&iaxs_lock);
                                return NULL;
                        }
                        c->nativeformats = native;
                }
        }
-       ast_pthread_mutex_unlock(&iaxs_lock);
        return c;
 }
 
@@ -3895,7 +4220,6 @@ static void *network_thread(void *ignore)
        struct ast_iax_frame *f, *freeme;
        /* Establish I/O callback for socket read */
        ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
-       ast_pthread_mutex_lock(&iaxs_lock);
        for(;;) {
                /* Go through the queue, sending messages which have not yet been
                   sent, and scheduling retransmissions if appropriate */
@@ -3905,7 +4229,7 @@ static void *network_thread(void *ignore)
                        freeme = NULL;
                        if (!f->sentyet) {
                                f->sentyet++;
-                               /* Send a copy immediately */
+                               /* Send a copy immediately -- errors here are ok, so don't bother locking */
                                if (iaxs[f->callno]) {
                                        send_packet(f);
                                } 
@@ -3936,10 +4260,10 @@ static void *network_thread(void *ignore)
                                ast_iax_frame_free(freeme);
                }
                ast_pthread_mutex_unlock(&iaxq.lock);
-               ast_pthread_mutex_unlock(&iaxs_lock);
                res = ast_sched_wait(sched);
+               if ((res > 1000) || (res < 0))
+                       res = 1000;
                res = ast_io_wait(io, res);
-               ast_pthread_mutex_lock(&iaxs_lock);
                if (res >= 0) {
                        ast_sched_runq(sched);
                }
@@ -3993,6 +4317,7 @@ static struct iax_peer *build_peer(char *name, struct ast_variable *v)
                peer = malloc(sizeof(struct iax_peer));
                memset(peer, 0, sizeof(struct iax_peer));
                peer->expire = -1;
+               peer->pokeexpire = -1;
        }
        if (peer) {
                if (!found) {
@@ -4075,6 +4400,15 @@ static struct iax_peer *build_peer(char *name, struct ast_variable *v)
                                strncpy(peer->inkeys, v->value, sizeof(peer->inkeys));
                        } else if (!strcasecmp(v->name, "outkey")) {
                                strncpy(peer->outkey, v->value, sizeof(peer->outkey));
+                       } else if (!strcasecmp(v->name, "qualify")) {
+                               if (!strcasecmp(v->value, "no")) {
+                                       peer->maxms = 0;
+                               } else if (!strcasecmp(v->value, "yes")) {
+                                       peer->maxms = DEFAULT_MAXMS;
+                               } else if (sscanf(v->value, "%d", &peer->maxms) != 1) {
+                                       ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of iax.conf\n", peer->name, v->lineno);
+                                       peer->maxms = 0;
+                               }
                        }
 
                        v=v->next;
@@ -4181,14 +4515,26 @@ void delete_users(void){
 void prune_peers(void){
        /* Prune peers who still are supposed to be deleted */
        struct iax_peer *peer, *peerlast, *peernext;
+       int x;
        ast_pthread_mutex_lock(&peerl.lock);
        peerlast = NULL;
        for (peer=peerl.peers;peer;) {
                peernext = peer->next;
                if (peer->delme) {
+                       for (x=0;x<AST_IAX_MAX_CALLS;x++) {
+                               ast_pthread_mutex_lock(&iaxsl[x]);
+                               if (iaxs[x] && (iaxs[x]->peerpoke == peer)) {
+                                       iax_destroy(x);
+                               }
+                               ast_pthread_mutex_unlock(&iaxsl[x]);
+                       }
                        /* 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)
+                               iax_destroy(peer->callno);
                        free(peer);
                        if (peerlast)
                                peerlast->next = peernext;
@@ -4270,7 +4616,9 @@ int set_config(char *config_file, struct sockaddr_in* sin){
                } else if (!strcasecmp(v->name, "register")) {
                        iax_register(v->value, v->lineno);
                } else if (!strcasecmp(v->name, "tos")) {
-                       if (!strcasecmp(v->value, "lowdelay"))
+                       if (sscanf(v->value, "%i", &format) == 1)
+                               tos = format & 0xff;
+                       else if (!strcasecmp(v->value, "lowdelay"))
                                tos = IPTOS_LOWDELAY;
                        else if (!strcasecmp(v->value, "throughput"))
                                tos = IPTOS_THROUGHPUT;
@@ -4364,8 +4712,12 @@ static int cache_get_callno(char *data)
        for (x=0;x<AST_IAX_MAX_CALLS; x++) {
                /* Look for an *exact match* call.  Once a call is negotiated, it can only
                   look up entries for a single context */
-               if (iaxs[x] && !strcasecmp(data, iaxs[x]->dproot))
+               ast_pthread_mutex_lock(&iaxsl[x]);
+               if (iaxs[x] && !strcasecmp(data, iaxs[x]->dproot)) {
+                       ast_pthread_mutex_unlock(&iaxsl[x]);
                        return x;
+               }
+               ast_pthread_mutex_unlock(&iaxsl[x]);
        }
        /* No match found, we need to create a new one */
        strncpy(st, data, sizeof(st)-1);
@@ -4393,18 +4745,17 @@ static int cache_get_callno(char *data)
                host = st;
        }
        /* Populate our address from the given */
-       if (create_addr(&sin, NULL, NULL, host, NULL)) {
+       if (create_addr(&sin, NULL, NULL, NULL, host, NULL)) {
                return -1;
        }
        ast_log(LOG_DEBUG, "host: %s, user: %s, password: %s, context: %s\n", host, username, password, context);
-       ast_pthread_mutex_lock(&iaxs_lock);
        callno = find_callno(-1, -1, &sin, NEW_FORCE);
-       ast_pthread_mutex_unlock(&iaxs_lock);
-       strncpy(iaxs[callno]->dproot, data, sizeof(iaxs[callno]->dproot)-1);
        if (callno < 0) {
                ast_log(LOG_WARNING, "Unable to create call\n");
                return -1;
        }
+       ast_pthread_mutex_lock(&iaxsl[callno]);
+       strncpy(iaxs[callno]->dproot, data, sizeof(iaxs[callno]->dproot)-1);
        iaxs[callno]->capability = IAX_CAPABILITY_FULLBANDWIDTH;
        MYSNPRINTF "exten=TBD;");
        if (context)
@@ -4424,6 +4775,7 @@ static int cache_get_callno(char *data)
                ast_verbose(VERBOSE_PREFIX_3 "Calling TBD using options '%s'\n", requeststr);
        /* Start the call going */
        send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_NEW, 0, requeststr, strlen(requeststr) + 1, -1);
+       ast_pthread_mutex_unlock(&iaxsl[callno]);
        return callno;
 }
 
@@ -4618,6 +4970,28 @@ static int iax_canmatch(struct ast_channel *chan, char *context, char *exten, in
        return res;
 }
 
+static int iax_matchmore(struct ast_channel *chan, char *context, char *exten, int priority, char *callerid, char *data)
+{
+       int res = 0;
+       struct iax_dpcache *dp;
+#if 0
+       ast_log(LOG_NOTICE, "iax_matchmore: con: %s, exten: %s, pri: %d, cid: %s, data: %s\n", context, exten, priority, callerid ? callerid : "<unknown>", data);
+#endif
+       if (priority != 1)
+               return 0;
+       ast_pthread_mutex_lock(&dpcache_lock);
+       dp = find_cache(chan, data, context, exten, priority);
+       if (dp) {
+               if (dp->flags & CACHE_FLAG_MATCHMORE)
+                       res= 1;
+       }
+       ast_pthread_mutex_unlock(&dpcache_lock);
+       if (!dp) {
+               ast_log(LOG_WARNING, "Unable to make DP cache\n");
+       }
+       return res;
+}
+
 static int iax_exec(struct ast_channel *chan, char *context, char *exten, int priority, char *callerid, int newstack, char *data)
 {
        char odata[256];
@@ -4668,13 +5042,16 @@ static struct ast_switch iax_switch =
        exists:                 iax_exists,
        canmatch:               iax_canmatch,
        exec:                   iax_exec,
+       matchmore:              iax_matchmore,
 };
 
 int load_module(void)
 {
        char *config = "iax.conf";
        int res = 0;
+       int x;
        struct iax_registry *reg;
+       struct iax_peer *peer;
        
        struct sockaddr_in sin;
        
@@ -4684,6 +5061,9 @@ int load_module(void)
        sin.sin_family = AF_INET;
        sin.sin_port = ntohs(AST_DEFAULT_IAX_PORTNO);
        sin.sin_addr.s_addr = INADDR_ANY;
+
+       for (x=0;x<AST_IAX_MAX_CALLS;x++)
+               ast_pthread_mutex_init(&iaxsl[x]);
        
        io = io_context_create();
        sched = sched_context_create();
@@ -4706,6 +5086,8 @@ int load_module(void)
        ast_cli_register(&cli_show_stats);
        ast_cli_register(&cli_show_cache);
 
+       ast_manager_register( "IAXpeers", 0, manager_iax_show_peers, "List IAX Peers" );
+
        set_config(config,&sin);
 
        if (ast_channel_register(type, tdesc, iax_capability, iax_request)) {
@@ -4745,6 +5127,10 @@ int load_module(void)
        }
        for (reg = registrations; reg; reg = reg->next)
                iax_do_register(reg);
+       ast_pthread_mutex_lock(&peerl.lock);
+       for (peer = peerl.peers; peer; peer = peer->next)
+               iax_poke_peer(peer);
+       ast_pthread_mutex_unlock(&peerl.lock);
        return res;
 }
 
@@ -4763,6 +5149,7 @@ int unload_module()
        for (x=0;x<AST_IAX_MAX_CALLS;x++)
                if (iaxs[x])
                        iax_destroy(x);
+       ast_manager_unregister( "IAXpeers" );
        ast_cli_unregister(&cli_show_users);
        ast_cli_unregister(&cli_show_channels);
        ast_cli_unregister(&cli_show_peers);
index 992aa07..ed0ed6f 100755 (executable)
@@ -5,6 +5,8 @@
 port = 5060                    ; Port to bind to
 bindaddr = 0.0.0.0             ; Address to bind to
 context = default              ; Default for incoming calls
+;tos=lowdelay
+;tos=184
 
 ;[snomsip]
 ;type=friend
@@ -25,6 +27,8 @@ context = default             ; Default for incoming calls
 ;username=cisco
 ;secret=blah
 ;host=dynamic
+;canreinvite=no                        ; Cisco poops on reinvite sometimes
+;qualify=200                   ; Qualify peer is no more than 200ms away
 ;defaultip=192.168.0.4
 
 ;[cisco1]
index 791e46e..8b21767 100755 (executable)
@@ -32,6 +32,8 @@ struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io);
 
 void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them);
 
+void ast_rtp_get_peer(struct ast_rtp *rpt, struct sockaddr_in *them);
+
 void ast_rtp_get_us(struct ast_rtp *rtp, struct sockaddr_in *us);
 
 void ast_rtp_destroy(struct ast_rtp *rtp);
@@ -42,6 +44,10 @@ void ast_rtp_set_data(struct ast_rtp *rtp, void *data);
 
 int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *f);
 
+int ast_rtp_senddigit(struct ast_rtp *rtp, char digit);
+
+int ast_rtp_settos(struct ast_rtp *rtp, int tos);
+
 int ast2rtp(int id);
 
 int rtp2ast(int id);