Version 0.2.0 from FTP
[asterisk/asterisk.git] / channels / chan_iax.c
index 77f8557..7f8e233 100755 (executable)
@@ -2,7 +2,7 @@
  * Asterisk -- A telephony toolkit for Linux.
  *
  * Implementation of Inter-Asterisk eXchange
- * 
+ *
  * Copyright (C) 1999, Mark Spencer
  *
  * Mark Spencer <markster@linux-support.net>
@@ -11,6 +11,7 @@
  * the GNU General Public License
  */
 
+#include <asterisk/lock.h>
 #include <asterisk/frame.h> 
 #include <asterisk/channel.h>
 #include <asterisk/channel_pvt.h>
 #include <asterisk/cli.h>
 #include <asterisk/translate.h>
 #include <asterisk/md5.h>
+#include <asterisk/cdr.h>
+#include <asterisk/crypto.h>
+#include <asterisk/acl.h>
 #include <arpa/inet.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <netinet/ip.h>
 #include <sys/time.h>
 #include <sys/signal.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <unistd.h>
 #include <netdb.h>
+#include <fcntl.h>
 
 #include "iax.h"
 
+/*
+ * Uncomment to try experimental IAX bridge optimization,
+ * designed to reduce latency when IAX calls cannot
+ * be trasnferred
+ */
+
+#define BRIDGE_OPTIMIZATION 
+
+
 #define DEFAULT_RETRY_TIME 1000
 #define MEMORY_SIZE 100
 #define DEFAULT_DROP 3
 
-/* If you want to use the simulator, then define IAX_SIMULATOR.  */
+#define DEBUG_SUPPORT
+
+/* Sample over last 100 units to determine historic jitter */
+#define GAMMA (0.01)
 
-/*
-#define IAX_SIMULATOR
-*/
 static char *desc = "Inter Asterisk eXchange";
 static char *tdesc = "Inter Asterisk eXchange Drver";
 static char *type = "IAX";
-static char *config = "iax.conf";
 
 static char context[80] = "default";
 
@@ -58,16 +72,26 @@ static int max_retries = 4;
 static int ping_time = 20;
 static int lagrq_time = 10;
 static int nextcallno = 0;
-static int maxjitterbuffer=4000;
+static int maxjitterbuffer=3000;
+
+static int iaxdefaultdpcache=30 * 60;  /* Cache dialplan entries for 30 minutes by default */
+
+static int iaxdefaulttimeout = 5;              /* Default to wait no more than 5 seconds for a reply to come back */
 
 static int netsocket = -1;
 
+static int tos = 0;
+
+static int expirey = AST_DEFAULT_REG_EXPIRE;
+
 static int usecnt;
-static pthread_mutex_t usecnt_lock = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t iaxs_lock = PTHREAD_MUTEX_INITIALIZER;
+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;
 
 /* Ethernet, etc */
-#define IAX_CAPABILITY_FULLBANDWIDTH   0x7FFFFFFF
+#define IAX_CAPABILITY_FULLBANDWIDTH   0xFFFF
 /* T1, maybe ISDN */
 #define IAX_CAPABILITY_MEDBANDWIDTH    (IAX_CAPABILITY_FULLBANDWIDTH & \
                                                                        ~AST_FORMAT_SLINEAR & \
@@ -90,21 +114,16 @@ static int iax_dropcount = DEFAULT_DROP;
 
 static int use_jitterbuffer = 1;
 
+static int iaxdebug = 0;
+
+static char accountcode[20];
+static int amaflags = 0;
+
 static pthread_t netthreadid;
 
 #define IAX_STATE_STARTED              (1 << 0)
 #define IAX_STATE_AUTHENTICATED (1 << 1)
-
-#define IAX_SENSE_DENY                 0
-#define IAX_SENSE_ALLOW                        1
-
-struct iax_ha {
-       /* Host access rule */
-       struct in_addr netaddr;
-       struct in_addr netmask;
-       int sense;
-       struct iax_ha *next;
-};
+#define IAX_STATE_TBD                  (1 << 2)
 
 struct iax_context {
        char context[AST_MAX_EXTENSION];
@@ -115,30 +134,75 @@ struct iax_user {
        char name[80];
        char secret[80];
        char methods[80];
-       struct iax_ha *ha;
+       char accountcode[20];
+       char inkeys[80];                                /* Key(s) this user can use to authenticate to us */
+       int amaflags;
+       int hascallerid;
+       char callerid[AST_MAX_EXTENSION];
+       struct ast_ha *ha;
        struct iax_context *contexts;
        struct iax_user *next;
 };
 
 struct iax_peer {
        char name[80];
-       char username[80];
+       char username[80];              
        char secret[80];
+       char outkey[80];                /* What key we use to talk to this peer */
+       char context[AST_MAX_EXTENSION];        /* Default context (for transfer really) */
        struct sockaddr_in addr;
        int formats;
        struct in_addr mask;
 
        /* Dynamic Registration fields */
-       int dynamic;
-       struct sockaddr_in defaddr;
-       char regsecret[80];
+       int dynamic;                                    /* If this is a dynamic peer */
+       struct sockaddr_in defaddr;             /* Default address if there is one */
+       char challenge[80];                             /* Challenge used to authenticate the secret */
        char methods[80];
-       struct timeval nexpire;
-       int expire;
-       struct iax_ha *ha;
+       char inkeys[80];                                /* Key(s) this peer can use to authenticate to us */
+
+       int hascallerid;
+       /* Suggested caller id if registering */
+       char callerid[AST_MAX_EXTENSION];
+       /* Whether or not to send ANI */
+       int sendani;
+       int expire;                                             /* Schedule entry for expirey */
+       int expirey;                                    /* How soon to expire */
+       int capability;                                 /* Capability */
+       int delme;                                              /* I need to be deleted */
+       struct ast_ha *ha;
        struct iax_peer *next;
 };
-       
+
+#define REG_STATE_UNREGISTERED 0
+#define REG_STATE_REGSENT         1
+#define REG_STATE_AUTHSENT        2
+#define REG_STATE_REGISTERED   3
+#define REG_STATE_REJECTED        4
+#define REG_STATE_TIMEOUT         5
+#define REG_STATE_NOAUTH          6
+
+#define TRANSFER_NONE                  0
+#define TRANSFER_BEGIN                 1
+#define TRANSFER_READY                 2
+#define TRANSFER_RELEASED              3
+#define TRANSFER_PASSTHROUGH   4
+
+struct iax_registry {
+       struct sockaddr_in addr;                /* Who we connect to for registration purposes */
+       char username[80];
+       char secret[80];                        /* Password or key name in []'s */
+       char random[80];
+       int expire;                                             /* Sched ID of expiration */
+       int refresh;                                    /* How often to refresh */
+       int regstate;
+       int callno;                                             /* Associated call number if applicable */
+       struct sockaddr_in us;                  /* Who the server thinks we are */
+       struct iax_registry *next;
+};
+
+struct iax_registry *registrations;
+
 /* Don't retry more frequently than every 10 ms, or less frequently than every 5 seconds */
 #define MIN_RETRY_TIME 10
 #define MAX_RETRY_TIME  10000
@@ -151,11 +215,14 @@ struct chan_iax_pvt {
        /* Pipes for communication.  pipe[1] belongs to the
           network thread (write), and pipe[0] belongs to the individual 
           channel (read) */
-       int pipe[2];
+       /* Whether or not we Quelch audio */
+       int quelch;
        /* Last received voice format */
        int voiceformat;
        /* Last sent voice format */
        int svoiceformat;
+       /* What we are capable of sending */
+       int capability;
        /* Last received timestamp */
        unsigned int last;
        /* Last sent timestamp - never send the same timestamp twice in a single call */
@@ -168,8 +235,10 @@ struct chan_iax_pvt {
        int callno;
        /* Peer callno */
        int peercallno;
-       /* Peer supported formats */
-       int peerformats;
+       /* Peer selected format */
+       int peerformat;
+       /* Peer capability */
+       int peercapability;
        /* timeval that we base our transmission on */
        struct timeval offset;
        /* timeval that we base our delivery on */
@@ -180,14 +249,18 @@ struct chan_iax_pvt {
        int jitterbuffer;
        /* Current jitter measure */
        int jitter;
+       /* Historic jitter value */
+       int historicjitter;
        /* LAG */
        int lag;
        /* Error, as discovered by the manager */
        int error;
-       /* Onwer if we have one */
+       /* Owner if we have one */
        struct ast_channel *owner;
        /* What's our state? */
        int state;
+       /* Expirey (optional) */
+       int expirey;
        /* Next outgoing sequence number */
        unsigned short oseqno;
        /* Next incoming sequence number */
@@ -198,6 +271,10 @@ struct chan_iax_pvt {
        char context[80];
        /* Caller ID if available */
        char callerid[80];
+       /* Hidden Caller ID (i.e. ANI) if appropriate */
+       char ani[80];
+       /* Whether or not ani should be transmitted in addition to Caller*ID */
+       int sendani;
        /* DNID */
        char dnid[80];
        /* Requested Extension */
@@ -210,8 +287,41 @@ struct chan_iax_pvt {
        char methods[80];
        /* MD5 challenge */
        char challenge[10];
+       /* Public keys permitted keys for incoming authentication */
+       char inkeys[80];
+       /* Private key for outgoing authentication */
+       char outkey[80];
+       /* Preferred language */
+       char language[80];
+       /* Associated registry */
+       struct iax_registry *reg;
+
+       /* Transferring status */
+       int transferring;
+       /* Already disconnected */
+       int alreadygone;
+       /* Who we are IAX transfering to */
+       struct sockaddr_in transfer;
+       /* What's the new call number for the transfer */
+       int transfercallno;
+
+       /* Status of knowledge of peer ADSI capability */
+       int peeradsicpe;
+       
+       /* Who we are bridged to */
+       int bridgecallno;
+       int pingid;
+       int lagid;
+       int autoid;
+       char dproot[AST_MAX_EXTENSION];
+       char accountcode[20];
+       int amaflags;
+       struct iax_dpcache *dpentries;
 };
 
+#define DIRECTION_INGRESS 1
+#define DIRECTION_OUTGRESS 2
+
 struct ast_iax_frame {
        /* Actual, isolated frame */
        struct ast_frame *f;
@@ -233,6 +343,16 @@ struct ast_iax_frame {
        int sentyet;
        /* Packet sequence number */
        int seqno;
+       /* Non-zero if should be sent to transfer peer */
+       int transfer;
+       /* Non-zero if this is the final message */
+       int final;
+       /* Ingress or outgres */
+       int direction;
+       /* Retransmission ID */
+       int retrans;
+
+
        /* Easy linking */
        struct ast_iax_frame *next;
        struct ast_iax_frame *prev;
@@ -255,18 +375,162 @@ static struct ast_peer_list {
        pthread_mutex_t lock;
 } peerl;
 
+/* Extension exists */
+#define CACHE_FLAG_EXISTS              (1 << 0)
+/* Extension is non-existant */
+#define CACHE_FLAG_NONEXISTANT (1 << 1)
+/* Extension can exist */
+#define CACHE_FLAG_CANEXIST            (1 << 2)
+/* Waiting to hear back response */
+#define CACHE_FLAG_PENDING             (1 << 3)
+/* Timed out */
+#define CACHE_FLAG_TIMEOUT             (1 << 4)
+/* Request transmitted */
+#define CACHE_FLAG_TRANSMITTED (1 << 5)
+/* Timeout */
+#define CACHE_FLAG_UNKNOWN             (1 << 6)
+
+static struct iax_dpcache {
+       char peercontext[AST_MAX_EXTENSION];
+       char exten[AST_MAX_EXTENSION];
+       struct timeval orig;
+       struct timeval expirey;
+       int flags;
+       int callno;
+       int waiters[256];
+       struct iax_dpcache *next;
+       struct iax_dpcache *peer;       /* For linking in peers */
+} *dpcache;
+
+pthread_mutex_t dpcache_lock;
+
+#ifdef DEBUG_SUPPORT
+void showframe(struct ast_iax_frame *f, struct ast_iax_full_hdr *fhi, int rx, struct sockaddr_in *sin)
+{
+       char *frames[] = {
+               "(0?)",
+               "DTMF   ",
+               "VOICE  ",
+               "VIDEO  ",
+               "CONTROL",
+               "NULL   ",
+               "IAX    ",
+               "TEXT   ",
+               "IMAGE  " };
+       char *iaxs[] = {
+               "(0?)",
+               "NEW    ",
+               "PING   ",
+               "PONG   ",
+               "ACK    ",
+               "HANGUP ",
+               "REJECT ",
+               "ACCEPT ",
+               "AUTHREQ",
+               "AUTHREP",
+               "INVAL  ",
+               "LAGRQ  ",
+               "LAGRP  ",
+               "REGREQ ",
+               "REGAUTH",
+               "REGACK ",
+               "REGREJ ",
+               "REGREL ",
+               "VNAK   ",
+               "DPREQ  ",
+               "DPREP  ",
+               "DIAL   ",
+               "TXREQ  ",
+               "TXCNT  ",
+               "TXACC  ",
+               "TXREADY",
+               "TXREL  ",
+               "TXREJ  ",
+       };
+       char *cmds[] = {
+               "(0?)",
+               "HANGUP ",
+               "RING   ",
+               "RINGING",
+               "ANSWER ",
+               "BUSY   ",
+               "TKOFFHK ",
+               "OFFHOOK" };
+       struct ast_iax_full_hdr *fh;
+       char retries[20];
+       char class2[20];
+       char subclass2[20];
+       char *class;
+       char *subclass;
+       if (f) {
+               fh = f->data;
+               snprintf(retries, sizeof(retries), "%03d", f->retries);
+       } else {
+               strcpy(retries, "N/A");
+               fh = fhi;
+       }
+       if (!(ntohs(fh->callno) & AST_FLAG_FULL)) {
+               /* Don't mess with mini-frames */
+               return;
+       }
+       if (fh->type > sizeof(frames)/sizeof(char *)) {
+               snprintf(class2, sizeof(class2), "(%d?)", fh->type);
+               class = class2;
+       } else {
+               class = frames[(int)fh->type];
+       }
+       if (fh->type == AST_FRAME_DTMF) {
+               sprintf(subclass2, "%c", fh->csub);
+               subclass = subclass2;
+       } else if (fh->type == AST_FRAME_IAX) {
+               if (fh->csub >= sizeof(iaxs)/sizeof(iaxs[0])) {
+                       snprintf(subclass2, sizeof(subclass2), "(%d?)", fh->csub);
+                       subclass = subclass2;
+               } else {
+                       subclass = iaxs[(int)fh->csub];
+               }
+       } else if (fh->type == AST_FRAME_CONTROL) {
+               if (fh->csub > sizeof(cmds)/sizeof(char *)) {
+                       snprintf(subclass2, sizeof(subclass2), "(%d?)", fh->csub);
+                       subclass = subclass2;
+               } else {
+                       subclass = cmds[(int)fh->csub];
+               }
+       } else {
+               snprintf(subclass2, sizeof(subclass2), "%d", fh->csub);
+               subclass = subclass2;
+       }
+       ast_verbose(
+"%s-Frame Retry[%s] -- Seqno: %2.2d  Type: %s Subclass: %s\n",
+       (rx ? "Rx" : "Tx"),
+       retries, ntohs(fh->seqno), class, subclass);
+               fprintf(stderr,
+"   Timestamp: %05dms  Callno: %5.5d  DCall: %5.5d [%s:%d]\n",
+       ntohl(fh->ts),
+       (short)(ntohs(fh->callno) & ~AST_FLAG_FULL), (short) ntohs(fh->dcallno),
+               inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
+}
+#endif
+
 /* XXX We probably should use a mutex when working with this XXX */
 static struct chan_iax_pvt *iaxs[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);
+static int send_command_final(struct chan_iax_pvt *, char, int, unsigned int, char *, int, int);
+static int send_command_transfer(struct chan_iax_pvt *, char, int, unsigned int, char *, int);
 
 static unsigned int calc_timestamp(struct chan_iax_pvt *p, unsigned int ts);
 
 static int send_ping(void *data)
 {
        int callno = (long)data;
+       /* Ping only if it's real, not if it's bridged */
        if (iaxs[callno]) {
-               send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_PING, 0, NULL, 0, -1);
+#ifdef BRIDGE_OPTIMIZATION
+               if (iaxs[callno]->bridgecallno < 0)
+#endif
+                       send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_PING, 0, NULL, 0, -1);
                return 1;
        } else
                return 0;
@@ -275,8 +539,12 @@ static int send_ping(void *data)
 static int send_lagrq(void *data)
 {
        int callno = (long)data;
+       /* Ping only if it's real not if it's bridged */
        if (iaxs[callno]) {
-               send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_LAGRQ, 0, NULL, 0, -1);
+#ifdef BRIDGE_OPTIMIZATION
+               if (iaxs[callno]->bridgecallno < 0)
+#endif         
+                       send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_LAGRQ, 0, NULL, 0, -1);
                return 1;
        } else
                return 0;
@@ -317,16 +585,15 @@ static struct chan_iax_pvt *new_iax(void)
        tmp = malloc(sizeof(struct chan_iax_pvt));
        if (tmp) {
                memset(tmp, 0, sizeof(struct chan_iax_pvt));
-               /* On my linux system, pipe's are more than 2x as fast as socketpairs */
-               if (pipe(tmp->pipe)) {
-                       ast_log(LOG_WARNING, "Unable to create pipe: %s\n", strerror(errno));
-                       free(tmp);
-                       return NULL;
-               }
                tmp->callno = -1;
                tmp->peercallno = -1;
-               /* strncpy(tmp->context, context, sizeof(tmp->context)); */
-               strncpy(tmp->exten, "s", sizeof(tmp->exten));
+               tmp->transfercallno = -1;
+               tmp->bridgecallno = -1;
+               tmp->pingid = -1;
+               tmp->lagid = -1;
+               tmp->autoid = -1;
+               /* strncpy(tmp->context, context, sizeof(tmp->context)-1); */
+               strncpy(tmp->exten, "s", sizeof(tmp->exten)-1);
        }
        return tmp;
 }
@@ -336,39 +603,72 @@ static int get_timelen(struct ast_frame *f)
        int timelen=0;
        switch(f->subclass) {
        case AST_FORMAT_G723_1:
-               timelen = 30;
+               timelen = 30 /* XXX Not necessarily true XXX */;
                break;
        case AST_FORMAT_GSM:
-               timelen = 20;
+               timelen = 20 * (f->datalen / 20);
                break;
        case AST_FORMAT_SLINEAR:
-               timelen = f->datalen / 8;
+               timelen = f->datalen / 16;
                break;
        case AST_FORMAT_LPC10:
                timelen = 22;
                timelen += ((char *)(f->data))[7] & 0x1;
                break;
+       case AST_FORMAT_ULAW:
+               timelen = f->datalen / 8;
+               break;
+       case AST_FORMAT_ADPCM:
+               timelen = f->datalen / 4;
+               break;
        default:
                ast_log(LOG_WARNING, "Don't know how to calculate timelen on %d packets\n", f->subclass);
        }
        return timelen;
 }
 
-#if 0
-static struct ast_iax_frame *iaxfrdup(struct ast_iax_frame *fr)
+static int frames = 0;
+static int iframes = 0;
+static int oframes = 0;
+
+static struct ast_iax_frame *ast_iax_frame_new(int direction)
 {
-       /* Malloc() a copy of a frame */
-       struct ast_iax_frame *new = malloc(sizeof(struct ast_iax_frame));
-       if (new) 
-               memcpy(new, fr, sizeof(struct ast_iax_frame));  
-       return new;
+       struct ast_iax_frame *fr;
+       fr = malloc(sizeof(struct ast_iax_frame));
+       if (fr) {
+               fr->direction = direction;
+               fr->retrans = -1;
+               frames++;
+               if (fr->direction == DIRECTION_INGRESS)
+                       iframes++;
+               else
+                       oframes++;
+       }
+       return fr;
+}
+
+static void ast_iax_frame_free(struct ast_iax_frame *fr)
+{
+       if (fr->retrans > -1)
+               ast_sched_del(sched, fr->retrans);
+       if (fr->direction == DIRECTION_INGRESS)
+               iframes--;
+       else if (fr->direction == DIRECTION_OUTGRESS)
+               oframes--;
+       else {
+               ast_log(LOG_WARNING, "Attempt to double free frame detected\n");
+               CRASH;
+               return;
+       }
+       fr->direction = 0;
+       free(fr);
+       frames--;
 }
-#endif
 
 static struct ast_iax_frame *iaxfrdup2(struct ast_iax_frame *fr, int ch)
 {
        /* Malloc() a copy of a frame */
-       struct ast_iax_frame *new = malloc(sizeof(struct ast_iax_frame));
+       struct ast_iax_frame *new = ast_iax_frame_new(DIRECTION_INGRESS);
        if (new) {
                memcpy(new, fr, sizeof(struct ast_iax_frame));  
                new->f = ast_frdup(fr->f);
@@ -383,6 +683,8 @@ static struct ast_iax_frame *iaxfrdup2(struct ast_iax_frame *fr, int ch)
                        new->data = NULL;
                        new->datalen = 0;
                }
+               new->direction = DIRECTION_INGRESS;
+               new->retrans = -1;
        }
        return new;
 }
@@ -391,6 +693,26 @@ static struct ast_iax_frame *iaxfrdup2(struct ast_iax_frame *fr, int ch)
 #define NEW_ALLOW      1
 #define NEW_FORCE      2
 
+static int match(struct sockaddr_in *sin, short callno, short dcallno, struct chan_iax_pvt *cur)
+{
+       if ((cur->addr.sin_addr.s_addr == sin->sin_addr.s_addr) &&
+               (cur->addr.sin_port == sin->sin_port)) {
+               /* This is the main host */
+               if ((cur->peercallno == callno) ||
+                       ((dcallno == cur->callno) && (cur->peercallno) == -1)) {
+                       /* That's us.  Be sure we keep track of the peer call number */
+                       return 1;
+               }
+       }
+       if ((cur->transfer.sin_addr.s_addr == sin->sin_addr.s_addr) &&
+           (cur->transfer.sin_port == sin->sin_port) && (cur->transferring)) {
+               /* We're transferring */
+               if (dcallno == cur->callno)
+                       return 1;
+       }
+       return 0;
+}
+
 static int find_callno(short callno, short dcallno ,struct sockaddr_in *sin, int new)
 {
        int res = -1;
@@ -401,12 +723,7 @@ static int find_callno(short callno, short dcallno ,struct sockaddr_in *sin, int
                for (x=0;x<AST_IAX_MAX_CALLS;x++) {
                        if (iaxs[x]) {
                                /* Look for an exact match */
-                               if ((sin->sin_port == iaxs[x]->addr.sin_port) &&
-                                   (sin->sin_addr.s_addr == iaxs[x]->addr.sin_addr.s_addr) &&
-                                       ((callno == iaxs[x]->peercallno) || /* Our expected source call number is the same */
-                                        ((dcallno == x) && (iaxs[x]->peercallno = -1)) 
-                                        /* We have no expected source number, and the destination is right */
-                                        )) {
+                               if (match(sin, callno, dcallno, iaxs[x])) {
                                        res = x;
                                        break;
                                }
@@ -416,7 +733,7 @@ static int find_callno(short callno, short dcallno ,struct sockaddr_in *sin, int
        if ((res < 0) && (new >= NEW_ALLOW)) {
                /* Create a new one */
                start = nextcallno;
-               for (x = nextcallno + 1; iaxs[x] && (x != start); x = (x + 1) % AST_IAX_MAX_CALLS) 
+               for (x = (nextcallno + 1) % AST_IAX_MAX_CALLS; iaxs[x] && (x != start); x = (x + 1) % AST_IAX_MAX_CALLS)
                if (x == start) {
                        ast_log(LOG_WARNING, "Unable to accept more calls\n");
                        return -1;
@@ -431,44 +748,52 @@ static int find_callno(short callno, short dcallno ,struct sockaddr_in *sin, int
                        iaxs[x]->peercallno = callno;
                        iaxs[x]->callno = x;
                        iaxs[x]->pingtime = DEFAULT_RETRY_TIME;
-                       ast_sched_add(sched, ping_time * 1000, send_ping, (void *)x);
-                       ast_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)x);
-               } else
-                       ast_log(LOG_DEBUG, "Out of memory\n");
+                       iaxs[x]->expirey = expirey;
+                       iaxs[x]->pingid = ast_sched_add(sched, ping_time * 1000, send_ping, (void *)x);
+                       iaxs[x]->lagid = ast_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)x);
+                       iaxs[x]->amaflags = amaflags;
+                       strncpy(iaxs[x]->accountcode, accountcode, sizeof(iaxs[x]->accountcode)-1);
+               } else {
+                       ast_log(LOG_WARNING, "Out of resources\n");
+                       return -1;
+               }
                res = x;
                nextcallno = x;
        }
        return res;
 }
 
-static int iax_send(struct chan_iax_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno);
+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)
 {
-       /* Just deliver the packet by writing it to half of the pipe. */
+       /* Just deliver the packet by using queueing */
        struct ast_iax_frame *fr = data;
        unsigned int ts;
-       if (iaxs[fr->callno]) {
+       fr->retrans = -1;
+       if (iaxs[fr->callno] && !iaxs[fr->callno]->alreadygone) {
                if (fr->f->frametype == AST_FRAME_IAX) {
                        /* We have to treat some of these packets specially because
                           they're LAG measurement packets */
                        if (fr->f->subclass == AST_IAX_COMMAND_LAGRQ) {
                                /* If we got a queued request, build a reply and send it */
                                fr->f->subclass = AST_IAX_COMMAND_LAGRP;
-                               iax_send(iaxs[fr->callno], fr->f, fr->ts, -1);
+                               iax_send(iaxs[fr->callno], fr->f, fr->ts, -1, 0, 0, 0);
                        } else if (fr->f->subclass == AST_IAX_COMMAND_LAGRP) {
                                /* This is a reply we've been given, actually measure the difference */
                                ts = calc_timestamp(iaxs[fr->callno], 0);
                                iaxs[fr->callno]->lag = ts - fr->ts;
                        }
                } else {
-                       ast_fr_fdwrite(iaxs[fr->callno]->pipe[1], fr->f);
+                       if (iaxs[fr->callno]->owner) {
+                               ast_queue_frame(iaxs[fr->callno]->owner, fr->f, 1);
+                       }
                }
        }
        /* Free the packet */
        ast_frfree(fr->f);
        /* And our iax frame */
-       free(fr);
+       ast_iax_frame_free(fr);
        /* And don't run again */
        return 0;
 }
@@ -507,11 +832,7 @@ static int handle_error()
        return 0;
 }
 
-#ifdef IAX_SIMULATOR
-static int __send_packet(struct ast_iax_frame *f)
-#else
 static int send_packet(struct ast_iax_frame *f)
-#endif
 {
        int res;
        if (option_debug)
@@ -521,160 +842,120 @@ static int send_packet(struct ast_iax_frame *f)
                ast_log(LOG_WARNING, "Call number = %d\n", f->callno);
                return -1;
        }
+       if (!iaxs[f->callno])
+               return -1;
        if (iaxs[f->callno]->error)
                return -1;
-       res = sendto(netsocket, f->data, f->datalen, 0, &iaxs[f->callno]->addr, 
+       if (f->transfer) {
+#ifdef DEBUG_SUPPORT
+               if (iaxdebug)
+                       showframe(f, NULL, 0, &iaxs[f->callno]->transfer);
+#endif
+               res = sendto(netsocket, f->data, f->datalen, 0,(struct sockaddr *)&iaxs[f->callno]->transfer,
+                                       sizeof(iaxs[f->callno]->transfer));
+       } else {
+#ifdef DEBUG_SUPPORT
+               if (iaxdebug)
+                       showframe(f, NULL, 0, &iaxs[f->callno]->addr);
+#endif
+               res = sendto(netsocket, f->data, f->datalen, 0,(struct sockaddr *)&iaxs[f->callno]->addr,
                                        sizeof(iaxs[f->callno]->addr));
-       if (res < 0) { 
-               ast_log(LOG_WARNING, "Received error: %s\n", strerror(errno));
+       }
+       if (res < 0) {
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Received error: %s\n", strerror(errno));
                handle_error();
-       } else 
+       } else
                res = 0;
        return res;
 }
 
-#ifdef IAX_SIMULATOR
-
-/* Average amount of delay in the connection */
-static int average_delay = 0;
-/* Permitted deviation either side of the average delay */
-static int delay_deviation = 0;
-/* Percent chance that a packet arrives O.K. */
-static int reliability = 100;
-
-static int iax_sim_calc_delay()
-{
-       int ms;
-       ms = average_delay - delay_deviation;
-       ms += ((float)(delay_deviation * 2)) * rand() / (RAND_MAX + 1.0);
-       if (ms < 0)
-               ms = 0;
-       if ((float)rand()/(RAND_MAX + 1.0) < ((float)reliability)/100)
-               return ms;
-       else
-               return -1;
-}
-
-static int d_send_packet(void *v)
-{
-       struct ast_iax_frame *f = (struct ast_iax_frame *)v;
-       if (iaxs[f->callno])
-               __send_packet(f);
-       ast_frfree(f->f);
-       free(f);
-       return 0;
-}
-
-static int send_packet(struct ast_iax_frame *f)
-{
-       struct ast_iax_frame *fn;
-       int ms;
-       ms = iax_sim_calc_delay();
-       if (ms == 0)
-               return __send_packet(f);
-       else if (ms > 0) {
-               /* Make a completely independent frame, in case the other
-                  is destroyed -- still doesn't make things like hangups
-                  arrive if the main channel is destroyed, but close enough */
-               fn = iaxfrdup2(f, 1);
-               ast_sched_add(sched, ms, d_send_packet, fn);
-       } /* else we drop the packet */
-       return 0;
-}
 
-static int iax_sim_set(int fd, int argc, char *argv[])
+static void iax_destroy(int callno)
 {
-       if (argc != 4)
-               return RESULT_SHOWUSAGE;
-       if (!strcasecmp(argv[2], "delay")) 
-               average_delay = atoi(argv[3]);
-       else if (!strcasecmp(argv[2], "deviation"))
-               delay_deviation = atoi(argv[3]);
-       else if (!strcasecmp(argv[2], "reliability"))
-               reliability = atoi(argv[3]);
-       else 
-               return RESULT_SHOWUSAGE;
-       if (reliability > 100)
-               reliability = 100;
-       if (reliability < 0)
-               reliability = 0;
-       if (delay_deviation > average_delay)
-               delay_deviation = average_delay;
-       return RESULT_SUCCESS;
-}
+       struct chan_iax_pvt *pvt = iaxs[callno];
+       struct ast_iax_frame *cur;
+       if (pvt) {
+               /* 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;
 
-static char delay_usage[] = 
-"Usage: sim set delay <value>\n"
-"       Configure the IAX network simulator to generate average\n"
-"       delays equal to the specified value (in milliseconds).\n";
+               /* Already gone */
+               pvt->alreadygone = 1;
 
-static char deviation_usage[] = 
-"Usage: sim set deviation <value>\n"
-"       Configures the IAX network simulator's deviation value.\n"
-"       The delays generated by the simulator will always be within\n"
-"       this value of milliseconds (postive or negative) of the \n"
-"       average delay.\n";
+               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);
+                       }
+               }
 
-static char reliability_usage[] = 
-"Usage: sim set reliability <value>\n"
-"       Configure the probability that a packet will be delivered.\n"
-"       The value specified is a percentage from 0 to 100\n";
+               iaxs[callno] = NULL;
 
-static int iax_sim_show(int fd, int argc, char *argv[])
-{
-       if (argc != 2)
-               return RESULT_SHOWUSAGE;
-       ast_cli(fd, "Average Delay:   %d ms\n", average_delay);
-       ast_cli(fd, "Delay Deviation: %d ms\n", delay_deviation);
-       ast_cli(fd, "Reliability:     %d %\n", reliability);
-       return RESULT_SUCCESS;
+               for (cur = iaxq.head; cur ; cur = cur->next) {
+                       /* Cancel any pending transmissions */
+                       if (cur->callno == pvt->callno) 
+                               cur->retries = -1;
+               }
+               if (pvt->reg) {
+                       pvt->reg->callno = -1;
+               }
+               free(pvt);
+       }
 }
 
-static char sim_show_usage[] = 
-"Usage: sim show\n"
-"       Displays average delay, deviation, and reliability\n"
-"       used by the network simulator.\n";
-
-static struct ast_cli_entry delay_cli = 
-{ { "sim", "set", "delay", NULL }, iax_sim_set, "Sets simulated average delay", delay_usage };
-static struct ast_cli_entry deviation_cli = 
-{ { "sim", "set", "deviation", NULL }, iax_sim_set, "Sets simulated delay deviation", deviation_usage };
-static struct ast_cli_entry reliability_cli = 
-{ { "sim", "set", "reliability", NULL }, iax_sim_set, "Sets simulated reliability", reliability_usage };
-static struct ast_cli_entry sim_show_cli = 
-{ { "sim", "show", NULL }, iax_sim_show, "Displays simulation parameters", sim_show_usage };
-
-#endif
 
 static int attempt_transmit(void *data)
 {
        /* Attempt to transmit the frame to the remote peer */
-       char zero = 0;
        struct ast_iax_frame *f = data;
-       int res = 0;
+       int freeme=0;
        /* Make sure this call is still active */
-       if (iaxs[f->callno]) {
-               if ((f->retries == -1) /* Already ACK'd */ ||
+       if ((f->callno > -1) && iaxs[f->callno]) {
+               if ((f->retries < 0) /* Already ACK'd */ ||
                    (f->retries >= max_retries) /* Too many attempts */) {
                                /* Record an error if we've transmitted too many times */
                                if (f->retries >= max_retries) {
-                                       ast_log(LOG_WARNING, "Max retries exceeded to host %s (type = %d, subclass = %d, ts=%d, seqno=%d)\n", inet_ntoa(iaxs[f->callno]->addr.sin_addr), f->f->frametype, f->f->subclass, f->ts, f->seqno);
-                                       iaxs[f->callno]->error = ETIMEDOUT;
-                                       /* Send a bogus frame to wake up the waiting process */
-                                       write(iaxs[f->callno]->pipe[1], &zero, 1);
+                                       if (f->transfer) {
+                                               /* Transfer timeout */
+                                               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);
+                                       } 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) {
+                                                       /* Hangup the fd */
+                                                       ast_softhangup(iaxs[f->callno]->owner, AST_SOFTHANGUP_DEV);
+                                                       ast_queue_hangup(iaxs[f->callno]->owner, 1);
+                                               } 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);
+                                               }
+                                       }
+
                                }
-                               /* Don't attempt delivery, just remove it from the queue */
-                               pthread_mutex_lock(&iaxq.lock);
-                               if (f->prev) 
-                                       f->prev->next = f->next;
-                               else
-                                       iaxq.head = f->next;
-                               if (f->next)
-                                       f->next->prev = f->prev;
-                               else
-                                       iaxq.tail = f->prev;
-                               iaxq.count--;
-                               pthread_mutex_unlock(&iaxq.lock);
+                               freeme++;
                } else {
                        /* Attempt transmission */
                        send_packet(f);
@@ -683,12 +964,36 @@ static int attempt_transmit(void *data)
                        f->retrytime *= 10;
                        if (f->retrytime > MAX_RETRY_TIME)
                                f->retrytime = MAX_RETRY_TIME;
-                       ast_sched_add(sched, f->retrytime, attempt_transmit, f);
-                       res=0;
+                       /* Transfer messages max out at one second */
+                       if (f->transfer && (f->retrytime > 1000))
+                               f->retrytime = 1000;
+                       f->retrans = ast_sched_add(sched, f->retrytime, attempt_transmit, f);
                }
+       } else {
+               /* Make sure it gets freed */
+               f->retries = -1;
+               freeme++;
        }
        /* Do not try again */
-       return res;
+       if (freeme) {
+               /* Don't attempt delivery, just remove it from the queue */
+               ast_pthread_mutex_lock(&iaxq.lock);
+               if (f->prev) 
+                       f->prev->next = f->next;
+               else
+                       iaxq.head = f->next;
+               if (f->next)
+                       f->next->prev = f->prev;
+               else
+                       iaxq.tail = f->prev;
+               iaxq.count--;
+               ast_pthread_mutex_unlock(&iaxq.lock);
+               /* Free the frame */
+               ast_frfree(f->f);
+               f->retrans = -1;
+               ast_iax_frame_free(f);
+       }
+       return 0;
 }
 
 static int iax_set_jitter(int fd, int argc, char *argv[])
@@ -701,7 +1006,7 @@ static int iax_set_jitter(int fd, int argc, char *argv[])
                        max_jitter_buffer = 0;
        } else {
                if (argc == 5) {
-                       pthread_mutex_lock(&iaxs_lock);
+                       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]);
@@ -711,7 +1016,7 @@ 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]));
-                       pthread_mutex_unlock(&iaxs_lock);
+                       ast_pthread_mutex_unlock(&iaxs_lock);
                }
        }
        return RESULT_SUCCESS;
@@ -724,20 +1029,139 @@ static char jitter_usage[] =
 "to establish the maximum excess jitter buffer that is permitted before the jitter\n"
 "buffer size is reduced.";
 
+static int iax_show_stats(int fd, int argc, char *argv[])
+{
+       struct ast_iax_frame *cur;
+       int cnt = 0, dead=0, final=0;
+       if (argc != 3)
+               return RESULT_SHOWUSAGE;
+       for (cur = iaxq.head; cur ; cur = cur->next) {
+               if (cur->retries < 0)
+                       dead++;
+               if (cur->final)
+                       final++;
+               cnt++;
+       }
+       ast_cli(fd, "    IAX Statistics\n");
+       ast_cli(fd, "---------------------\n");
+       ast_cli(fd, "Outstanding frames: %d (%d ingress, %d outgress)\n", frames, iframes, oframes);
+       ast_cli(fd, "Packets in transmit queue: %d dead, %d final, %d total\n", dead, final, cnt);
+       return RESULT_SUCCESS;
+}
+
+static int iax_show_cache(int fd, int argc, char *argv[])
+{
+       struct iax_dpcache *dp;
+       char tmp[1024], *pc;
+       int s;
+       int x,y;
+       struct timeval tv;
+       gettimeofday(&tv, NULL);
+       ast_pthread_mutex_lock(&dpcache_lock);
+       dp = dpcache;
+       ast_cli(fd, "%-20.20s %-12.12s %-9.9s %-8.8s %s\n", "Peer/Context", "Exten", "Exp.", "Wait.", "Flags");
+       while(dp) {
+               s = dp->expirey.tv_sec - tv.tv_sec;
+               strcpy(tmp, "");
+               if (dp->flags & CACHE_FLAG_EXISTS)
+                       strcat(tmp, "EXISTS|");
+               if (dp->flags & CACHE_FLAG_NONEXISTANT)
+                       strcat(tmp, "NONEXISTANT|");
+               if (dp->flags & CACHE_FLAG_CANEXIST)
+                       strcat(tmp, "CANEXIST|");
+               if (dp->flags & CACHE_FLAG_PENDING)
+                       strcat(tmp, "PENDING|");
+               if (dp->flags & CACHE_FLAG_TIMEOUT)
+                       strcat(tmp, "TIMEOUT|");
+               if (dp->flags & CACHE_FLAG_TRANSMITTED)
+                       strcat(tmp, "TRANSMITTED|");
+               if (dp->flags & CACHE_FLAG_UNKNOWN)
+                       strcat(tmp, "UNKNOWN|");
+               /* Trim trailing pipe */
+               if (strlen(tmp))
+                       tmp[strlen(tmp) - 1] = '\0';
+               else
+                       strcpy(tmp, "(none)");
+               y=0;
+               pc = strchr(dp->peercontext, '@');
+               if (!pc)
+                       pc = dp->peercontext;
+               else
+                       pc++;
+               for (x=0;x<sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++)
+                       if (dp->waiters[x] > -1)
+                               y++;
+               if (s > 0)
+                       ast_cli(fd, "%-20.20s %-12.12s %-9d %-8d %s\n", pc, dp->exten, s, y, tmp);
+               else
+                       ast_cli(fd, "%-20.20s %-12.12s %-9.9s %-8d %s\n", pc, dp->exten, "(expired)", y, tmp);
+               dp = dp->next;
+       }
+       ast_pthread_mutex_unlock(&dpcache_lock);
+       return RESULT_SUCCESS;
+}
+
+static char show_stats_usage[] =
+"Usage: iax show stats\n"
+"       Display statistics on IAX channel driver.\n";
+
+
+static char show_cache_usage[] =
+"Usage: iax show cache\n"
+"       Display currently cached IAX Dialplan results.\n";
+
 static struct ast_cli_entry cli_set_jitter = 
 { { "iax", "set", "jitter", NULL }, iax_set_jitter, "Sets IAX jitter buffer", jitter_usage };
 
+static struct ast_cli_entry cli_show_stats =
+{ { "iax", "show", "stats", NULL }, iax_show_stats, "Display IAX statistics", show_stats_usage };
+
+static struct ast_cli_entry cli_show_cache =
+{ { "iax", "show", "cache", NULL }, iax_show_cache, "Display IAX cached dialplan", show_cache_usage };
+
 static unsigned int calc_rxstamp(struct chan_iax_pvt *p);
 
-static int schedule_delivery(struct ast_iax_frame *fr)
+#ifdef BRIDGE_OPTIMIZATION
+static unsigned int calc_fakestamp(struct chan_iax_pvt *from, struct chan_iax_pvt *to, unsigned int ts);
+
+static int forward_delivery(struct ast_iax_frame *fr)
+{
+       struct chan_iax_pvt *p1, *p2;
+       p1 = iaxs[fr->callno];
+       p2 = iaxs[p1->bridgecallno];
+       if (!p1)
+               return -1;
+       if (!p2)
+               return -1;
+       /* Fix relative timestamp */
+       fr->ts = calc_fakestamp(p1, p2, fr->ts);
+       /* Now just send it send on the 2nd one 
+          with adjusted timestamp */
+       return iax_send(p2, fr->f, fr->ts, -1, 0, 0, 0);
+}
+#endif
+
+static int schedule_delivery(struct ast_iax_frame *fr, int reallydeliver)
 {
-       /* XXX FIXME: I should delay delivery with a sliding jitter buffer XXX */
        int ms,x;
        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.  */
        ms = calc_rxstamp(iaxs[fr->callno]) - fr->ts;
+
+       if (ms > 32767) {
+               /* What likely happened here is that our counter has circled but we haven't
+                  gotten the update from the main packet.  We'll just pretend that we did, and
+                  update the timestamp appropriately. */
+               ms -= 65536;
+       }
+
+       if (ms < -32768) {
+               /* We got this packet out of order.  Lets add 65536 to it to bring it into our new
+                  time frame */
+               ms += 65536;
+       }
        
        /* Rotate our history queue of "lateness".  Don't worry about those initial
           zeros because the first entry will always be zero */
@@ -750,7 +1174,7 @@ static int schedule_delivery(struct ast_iax_frame *fr)
           work to do the same for the maximum, repeatedly */
        min=iaxs[fr->callno]->history[0];
        for (z=0;z < iax_dropcount + 1;z++) {
-               /* Start very pessimistic ;-) */
+               /* Start very optimistic ;-) */
                max=-999999999;
                for (x=0;x<MEMORY_SIZE;x++) {
                        if (max < iaxs[fr->callno]->history[x]) {
@@ -780,11 +1204,21 @@ static int schedule_delivery(struct ast_iax_frame *fr)
           earliest and the latest. */
        iaxs[fr->callno]->jitter = max - min;   
        
+       /* IIR filter for keeping track of historic jitter, but always increase
+          historic jitter immediately for increase */
+       
+       if (iaxs[fr->callno]->jitter > iaxs[fr->callno]->historicjitter )
+               iaxs[fr->callno]->historicjitter = iaxs[fr->callno]->jitter;
+       else
+               iaxs[fr->callno]->historicjitter = GAMMA * (double)iaxs[fr->callno]->jitter + (1-GAMMA) * 
+                       iaxs[fr->callno]->historicjitter;
+
        /* If our jitter buffer is too big (by a significant margin), then we slowly
           shrink it by about 1 ms each time to avoid letting the change be perceived */
        if (max < iaxs[fr->callno]->jitterbuffer - max_jitter_buffer)
                iaxs[fr->callno]->jitterbuffer -= 2;
 
+
 #if 1
        /* Constrain our maximum jitter buffer appropriately */
        if (max > min + maxjitterbuffer) {
@@ -811,23 +1245,28 @@ static int schedule_delivery(struct ast_iax_frame *fr)
        if (!use_jitterbuffer)
                ms = 0;
 
+       /* If the caller just wanted us to update, return now */
+       if (!reallydeliver)
+               return 0;
+               
        if (ms < 1) {
                if (option_debug)
                        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);
-               }
-               else {
+               } else {
+                       if (option_debug)
+                               ast_log(LOG_DEBUG, "Dropping voice packet since %d ms is, too old\n", ms);
                        /* Free the packet */
                        ast_frfree(fr->f);
                        /* And our iax frame */
-                       free(fr);
+                       ast_iax_frame_free(fr);
                }
        } else {
                if (option_debug)
                        ast_log(LOG_DEBUG, "Scheduling delivery in %d ms\n", ms);
-               ast_sched_add(sched, ms, do_deliver, fr);
+               fr->retrans = ast_sched_add(sched, ms, do_deliver, fr);
        }
        return 0;
 }
@@ -840,7 +1279,7 @@ static int iax_transmit(struct ast_iax_frame *fr)
        /* By setting this to 0, the network thread will send it for us, and
           queue retransmission if necessary */
        fr->sentyet = 0;
-       pthread_mutex_lock(&iaxq.lock);
+       ast_pthread_mutex_lock(&iaxq.lock);
        if (!iaxq.head) {
                /* Empty queue */
                iaxq.head = fr;
@@ -852,7 +1291,7 @@ static int iax_transmit(struct ast_iax_frame *fr)
                iaxq.tail = fr;
        }
        iaxq.count++;
-       pthread_mutex_unlock(&iaxq.lock);
+       ast_pthread_mutex_unlock(&iaxq.lock);
        /* Wake up the network thread */
        pthread_kill(netthreadid, SIGURG);
        return 0;
@@ -872,31 +1311,69 @@ static int iax_sendtext(struct ast_channel *c, char *text)
                0, 0, text, strlen(text) + 1, -1);
 }
 
-static int create_addr(struct sockaddr_in *sin, char *peer)
+static int iax_sendimage(struct ast_channel *c, struct ast_frame *img)
+{
+       return send_command(c->pvt->pvt, AST_FRAME_IMAGE, img->subclass, 0, img->data, img->datalen, -1);
+}
+
+static int iax_sendhtml(struct ast_channel *c, int subclass, char *data, int datalen)
+{
+       return send_command(c->pvt->pvt, AST_FRAME_HTML, subclass, 0, data, datalen, -1);
+}
+
+static int iax_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan)
+{
+       struct chan_iax_pvt *pvt = newchan->pvt->pvt;
+       pvt->owner = newchan;
+       return 0;
+}
+
+static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, char *peer, char *context)
 {
        struct hostent *hp;
        struct iax_peer *p;
+       int found=0;
+       if (sendani)
+               *sendani = 0;
        sin->sin_family = AF_INET;
-       pthread_mutex_lock(&peerl.lock);
+       ast_pthread_mutex_lock(&peerl.lock);
        p = peerl.peers;
        while(p) {
                if (!strcasecmp(p->name, peer)) {
-                       sin->sin_addr = p->addr.sin_addr;
-                       sin->sin_port = p->addr.sin_port;
-                       break;
+                       found++;
+                       if (capability)
+                               *capability = p->capability;
+                       if (p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) {
+                               if (sendani)
+                                       *sendani = p->sendani;
+                               if (context)
+                                       strncpy(context, p->context, AST_MAX_EXTENSION - 1);
+                               if (p->addr.sin_addr.s_addr) {
+                                       sin->sin_addr = p->addr.sin_addr;
+                                       sin->sin_port = p->addr.sin_port;
+                               } else {
+                                       sin->sin_addr = p->defaddr.sin_addr;
+                                       sin->sin_port = p->defaddr.sin_port;
+                               }
+                               break;
+                       }
                }
                p = p->next;
        }
-       pthread_mutex_unlock(&peerl.lock);
-       if (!p) {
+       ast_pthread_mutex_unlock(&peerl.lock);
+       if (!p && !found) {
                hp = gethostbyname(peer);
                if (hp) {
                        memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
                        sin->sin_port = htons(AST_DEFAULT_IAX_PORTNO);
                        return 0;
-               } else
+               } else {
+                       ast_log(LOG_WARNING, "No such host: %s\n", peer);
                        return -1;
-       } else
+               }
+       } else if (!p)
+               return -1;
+       else
                return 0;
 }
 static int iax_call(struct ast_channel *c, char *dest, int timeout)
@@ -906,15 +1383,18 @@ static int iax_call(struct ast_channel *c, char *dest, int timeout)
        char *rdest;
        char *rcontext;
        char *username;
+       char *secret = NULL;
        char *hname;
        char requeststr[256] = "";
        char myrdest [5] = "s";
+       char context[AST_MAX_EXTENSION] ="";
        char *portno = NULL;
-       if ((c->state != AST_STATE_DOWN) && (c->state != AST_STATE_RESERVED)) {
+       struct chan_iax_pvt *p = c->pvt->pvt;
+       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));
+       strncpy(host, dest, sizeof(host)-1);
        strtok(host, "/");
        /* If no destination extension specified, use 's' */
        rdest = strtok(NULL, "/");
@@ -931,30 +1411,53 @@ static int iax_call(struct ast_channel *c, char *dest, int timeout)
        } else {
                hname = host;
        }
+       if (username) {
+               username = strtok(username, ":");
+               secret = strtok(NULL, ":");
+       }
        if (strtok(hname, ":")) {
                strtok(hname, ":");
                portno = strtok(hname, ":");
        }
-       if (create_addr(&sin, hname)) {
+       if (create_addr(&sin, NULL, NULL, hname, context)) {
                ast_log(LOG_WARNING, "No address associated with '%s'\n", hname);
                return -1;
        }
+       /* Keep track of the context for outgoing calls too */
+       strncpy(c->context, context, sizeof(c->context) - 1);
        if (portno) {
                sin.sin_port = htons(atoi(portno));
        }
        /* Now we build our request string */
 #define MYSNPRINTF snprintf(requeststr + strlen(requeststr), sizeof(requeststr) - strlen(requeststr), 
+#define MYSNPRINTF2 snprintf(requeststr + strlen(requeststr), reqsize - strlen(requeststr), 
        MYSNPRINTF "exten=%s;", rdest);
        if (c->callerid)
                MYSNPRINTF "callerid=%s;", c->callerid);
+       if (p->sendani && c->ani)
+               MYSNPRINTF "ani=%s;", c->ani);
+       if (c->language && strlen(c->language))
+               MYSNPRINTF "language=%s;", c->language);
        if (c->dnid)
                MYSNPRINTF "dnid=%s;", c->dnid);
        if (rcontext)
                MYSNPRINTF "context=%s;", rcontext);
        if (username)
                MYSNPRINTF "username=%s;", username);
-       MYSNPRINTF "formats=%d;", c->format);
+       if (secret) {
+               if (secret[0] == '[') {
+                       /* This is an RSA key, not a normal secret */
+                       strncpy(p->outkey, secret + 1, sizeof(p->secret)-1);
+                       if (strlen(p->outkey)) {
+                               p->outkey[strlen(p->outkey) - 1] = '\0';
+                       }
+               } else
+                       strncpy(p->secret, secret, sizeof(p->secret)-1);
+       }
+       MYSNPRINTF "formats=%d;", c->nativeformats);
+       MYSNPRINTF "capability=%d;", p->capability);
        MYSNPRINTF "version=%d;", AST_IAX_PROTO_VERSION);
+       MYSNPRINTF "adsicpe=%d;", c->adsicpe);
        /* Trim the trailing ";" */
        if (strlen(requeststr))
                requeststr[strlen(requeststr) - 1] = '\0';
@@ -963,71 +1466,220 @@ static int iax_call(struct ast_channel *c, char *dest, int timeout)
                ast_verbose(VERBOSE_PREFIX_3 "Calling using options '%s'\n", requeststr);
        send_command((struct chan_iax_pvt *)c->pvt->pvt, AST_FRAME_IAX,
                AST_IAX_COMMAND_NEW, 0, requeststr, strlen(requeststr) + 1, -1);
-       c->state = AST_STATE_RINGING;
+       ast_setstate(c, AST_STATE_RINGING);
        return 0;
 }
 
-static void iax_destroy(int callno)
+static int iax_predestroy(struct chan_iax_pvt *pvt)
 {
-       char zero=0;
-       struct chan_iax_pvt *pvt = iaxs[callno];
-       if (pvt) {
-               if (pvt->owner) {
-                       /* If there's an owner, prod it to give up */
-                       write(pvt->pipe[1], &zero, 1);
-                       return;
-               }
-               iaxs[callno] = NULL;
-               close(pvt->pipe[0]);
-               close(pvt->pipe[1]);
-               free(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) {
+static int iax_hangup(struct ast_channel *c) 
+{
        struct chan_iax_pvt *pvt = c->pvt->pvt;
-       /* Send the hangup unless we have had a transmission error */
-       if (!pvt->error) {
-               send_command(pvt, AST_FRAME_IAX, AST_IAX_COMMAND_HANGUP, 0, NULL, 0, -1);
-               /* Wait for the network thread to transmit our command -- of course, if
-                  it doesn't, that's okay too -- the other end will find out
-                  soon enough, but it's a nicity if it can know now.  */
-               sleep(1);
-       }
-       pthread_mutex_lock(&iaxs_lock);
-       c->pvt->pvt = NULL;
-       pvt->owner = NULL;
-       pthread_mutex_lock(&usecnt_lock);
-       usecnt--;
-       if (usecnt < 0) 
-               ast_log(LOG_WARNING, "Usecnt < 0???\n");
-       pthread_mutex_unlock(&usecnt_lock);
-       ast_update_use_count();
+       int alreadygone;
+       if (pvt) {
+               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);
+               /* If we were already gone to begin with, destroy us now */
+               if (alreadygone) {
+                       iax_destroy(pvt->callno);
+               }
+       }
        if (option_verbose > 2) 
-               ast_verbose( VERBOSE_PREFIX_3 "Hungup '%s'\n", c->name);
-       iax_destroy(pvt->callno);
-       pthread_mutex_unlock(&iaxs_lock);
+               ast_verbose(VERBOSE_PREFIX_3 "Hungup '%s'\n", c->name);
        return 0;
 }
 
+static int iax_setoption(struct ast_channel *c, int option, void *data, int datalen)
+{
+       struct ast_option_header *h;
+       int res;
+       h = malloc(datalen + sizeof(struct ast_option_header));
+       if (h) {
+               h->flag = AST_OPTION_FLAG_REQUEST;
+               h->option = htons(option);
+               memcpy(h->data, data, datalen);
+               res = send_command((struct chan_iax_pvt *)c->pvt->pvt, AST_FRAME_CONTROL,
+                       AST_CONTROL_OPTION, 0, (char *)h, datalen + sizeof(struct ast_option_header), -1);
+               free(h);
+               return res;
+       } else 
+               ast_log(LOG_WARNING, "Out of memory\n");
+       return -1;
+}
 static struct ast_frame *iax_read(struct ast_channel *c) 
 {
-       struct chan_iax_pvt *pvt = c->pvt->pvt;
-       struct ast_frame *f;
-       if (pvt->error) {
-               ast_log(LOG_DEBUG, "Connection closed, error: %s\n", strerror(pvt->error));
-               return NULL;
-       }
-       f = ast_fr_fdread(pvt->pipe[0]);
-       if (f) {
-               if ((f->frametype == AST_FRAME_CONTROL) &&
-                   (f->subclass == AST_CONTROL_ANSWER))
-                               c->state = AST_STATE_UP;
-       }
-       return f;
+       static struct ast_frame f = { AST_FRAME_NULL, };
+       ast_log(LOG_NOTICE, "I should never be called!\n");
+       return &f;
 }
 
-static int iax_answer(struct ast_channel *c)
+static int iax_start_transfer(struct ast_channel *c0, struct ast_channel *c1)
+{
+       int res;
+       char req0[256];
+       char req1[256];
+       struct chan_iax_pvt *p0 = c0->pvt->pvt;
+       struct chan_iax_pvt *p1 = c1->pvt->pvt;
+       snprintf(req0, sizeof(req0), "remip=%s;remport=%d;remcall=%d;", inet_ntoa(p1->addr.sin_addr), ntohs(p1->addr.sin_port), p1->peercallno);
+       snprintf(req1, sizeof(req1), "remip=%s;remport=%d;remcall=%d;", inet_ntoa(p0->addr.sin_addr), ntohs(p0->addr.sin_port), p0->peercallno);
+       res = send_command(p0, AST_FRAME_IAX, AST_IAX_COMMAND_TXREQ, 0, req0, strlen(req0) + 1, -1);
+       if (res)
+               return -1;
+       res = send_command(p1, AST_FRAME_IAX, AST_IAX_COMMAND_TXREQ, 0, req1, strlen(req1) + 1, -1);
+       if (res)
+               return -1;
+       p0->transferring = TRANSFER_BEGIN;
+       p1->transferring = TRANSFER_BEGIN;
+       return 0;
+}
+
+static int iax_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc)
+{
+       struct ast_channel *cs[3];
+       struct ast_channel *who;
+       int to = -1;
+       int res;
+       int transferstarted=0;
+       struct ast_frame *f;
+       struct chan_iax_pvt *p0 = c0->pvt->pvt;
+       struct chan_iax_pvt *p1 = c1->pvt->pvt;
+
+       /* Put them in native bridge mode */
+       p0->bridgecallno = p1->callno;
+       p1->bridgecallno = p0->callno;
+
+       /* If not, try to bridge until we can execute a transfer, if we can */
+       cs[0] = c0;
+       cs[1] = c1;
+       for (/* ever */;;) {
+               /* Check in case we got masqueraded into */
+               if ((c0->type != type) || (c1->type != type)) {
+                       if (option_verbose > 2)
+                               ast_verbose(VERBOSE_PREFIX_3 "Can't masquerade, we're different...\n");
+                       return -2;
+               }
+               if (!transferstarted) {
+                       /* Try the transfer */
+                       if (iax_start_transfer(c0, c1))
+                               ast_log(LOG_WARNING, "Unable to start the transfer\n");
+                       transferstarted = 1;
+               }
+               
+               if ((p0->transferring == TRANSFER_RELEASED) && (p1->transferring == TRANSFER_RELEASED)) {
+                       /* Call has been transferred.  We're no longer involved */
+                       sleep(1);
+                       c0->_softhangup |= AST_SOFTHANGUP_DEV;
+                       c1->_softhangup |= AST_SOFTHANGUP_DEV;
+                       *fo = NULL;
+                       *rc = c0;
+                       res = 0;
+                       break;
+               }
+               to = 1000;
+               who = ast_waitfor_n(cs, 2, &to);
+               if (!who) {
+                       continue;
+               }
+               f = ast_read(who);
+               if (!f) {
+                       *fo = NULL;
+                       *rc = who;
+                       res = 0;
+                       break;
+               }
+               if ((f->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) {
+                       *fo = f;
+                       *rc = who;
+                       res =  0;
+                       break;
+               }
+               if ((f->frametype == AST_FRAME_VOICE) ||
+                       (f->frametype == AST_FRAME_TEXT) ||
+                       (f->frametype == AST_FRAME_VIDEO) || 
+                       (f->frametype == AST_FRAME_IMAGE) ||
+                       (f->frametype == AST_FRAME_DTMF)) {
+                       if ((f->frametype == AST_FRAME_DTMF) && 
+                               (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))) {
+                               if ((who == c0)) {
+                                       if  ((flags & AST_BRIDGE_DTMF_CHANNEL_0)) {
+                                               *rc = c0;
+                                               *fo = f;
+                                               /* Take out of conference mode */
+                                               res = 0;
+                                               break;
+                                       } else 
+                                               goto tackygoto;
+                               } else
+                               if ((who == c1)) {
+                                       if (flags & AST_BRIDGE_DTMF_CHANNEL_1) {
+                                               *rc = c1;
+                                               *fo = f;
+                                               res =  0;
+                                               break;
+                                       } else
+                                               goto tackygoto;
+                               }
+                       } else {
+#if 0
+                               ast_log(LOG_DEBUG, "Read from %s\n", who->name);
+                               if (who == last) 
+                                       ast_log(LOG_DEBUG, "Servicing channel %s twice in a row?\n", last->name);
+                               last = who;
+#endif
+tackygoto:
+                               if (who == c0) 
+                                       ast_write(c1, f);
+                               else 
+                                       ast_write(c0, f);
+                       }
+                       ast_frfree(f);
+               } else
+                       ast_frfree(f);
+               /* Swap who gets priority */
+               cs[2] = cs[0];
+               cs[0] = cs[1];
+               cs[1] = cs[2];
+       }
+       p0->bridgecallno = -1;
+       p1->bridgecallno = -1;
+       return res;
+}
+
+static int iax_answer(struct ast_channel *c)
 {
        struct chan_iax_pvt *pvt = c->pvt->pvt;
        if (option_debug)
@@ -1035,37 +1687,88 @@ static int iax_answer(struct ast_channel *c)
        return send_command(pvt, AST_FRAME_CONTROL, AST_CONTROL_ANSWER, 0, NULL, 0, -1);
 }
 
+static int iax_indicate(struct ast_channel *c, int condition)
+{
+       struct chan_iax_pvt *pvt = c->pvt->pvt;
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Indicating condition %d\n", condition);
+       return send_command(pvt, AST_FRAME_CONTROL, condition, 0, NULL, 0, -1);
+}
+       
+
 static int iax_write(struct ast_channel *c, struct ast_frame *f);
 
-static struct ast_channel *ast_iax_new(struct chan_iax_pvt *i, int state)
+static int iax_getpeername(struct sockaddr_in sin, char *host, int len)
+{
+       struct iax_peer *peer;
+       int res = 0;
+       ast_pthread_mutex_lock(&peerl.lock);
+       peer = peerl.peers;
+       while(peer) {
+               if ((peer->addr.sin_addr.s_addr == sin.sin_addr.s_addr) &&
+                               (peer->addr.sin_port == sin.sin_port)) {
+                                       strncpy(host, peer->name, len-1);
+                                       res = 1;
+                                       break;
+               }
+               peer = peer->next;
+       }
+       ast_pthread_mutex_unlock(&peerl.lock);
+       return res;
+}
+
+static struct ast_channel *ast_iax_new(struct chan_iax_pvt *i, int state, int capability)
 {
+       char host[256];
        struct ast_channel *tmp;
-       tmp = ast_channel_alloc();
+       tmp = ast_channel_alloc(1);
        if (tmp) {
-               snprintf(tmp->name, sizeof(tmp->name), "IAX[%s:%d]/%d", inet_ntoa(i->addr.sin_addr), ntohs(i->addr.sin_port), i->callno);
+               if (!iax_getpeername(i->addr, host, sizeof(host)))
+                       snprintf(host, sizeof(host), "%s:%d", inet_ntoa(i->addr.sin_addr), ntohs(i->addr.sin_port));
+               if (strlen(i->username))
+                       snprintf(tmp->name, sizeof(tmp->name), "IAX[%s@%s]/%d", i->username, host, i->callno);
+               else
+                       snprintf(tmp->name, sizeof(tmp->name), "IAX[%s]/%d", host, i->callno);
                tmp->type = type;
-               tmp->fd = i->pipe[0];
                /* We can support any format by default, until we get restricted */
-               tmp->format = iax_capability;
+               tmp->nativeformats = capability;
+               tmp->readformat = 0;
+               tmp->writeformat = 0;
                tmp->pvt->pvt = i;
                tmp->pvt->send_digit = iax_digit;
                tmp->pvt->send_text = iax_sendtext;
+               tmp->pvt->send_image = iax_sendimage;
+               tmp->pvt->send_html = iax_sendhtml;
                tmp->pvt->call = iax_call;
                tmp->pvt->hangup = iax_hangup;
                tmp->pvt->answer = iax_answer;
                tmp->pvt->read = iax_read;
                tmp->pvt->write = iax_write;
+               tmp->pvt->indicate = iax_indicate;
+               tmp->pvt->setoption = iax_setoption;
+               tmp->pvt->bridge = iax_bridge;
                if (strlen(i->callerid))
                        tmp->callerid = strdup(i->callerid);
+               if (strlen(i->ani))
+                       tmp->ani = strdup(i->ani);
+               if (strlen(i->language))
+                       strncpy(tmp->language, i->language, sizeof(tmp->language)-1);
                if (strlen(i->dnid))
                        tmp->dnid = strdup(i->dnid);
-               strncpy(tmp->context, i->context, sizeof(tmp->context));
-               strncpy(tmp->exten, i->exten, sizeof(tmp->exten));
+               if (strlen(i->accountcode))
+                       strncpy(tmp->accountcode, i->accountcode, sizeof(tmp->accountcode)-1);
+               if (i->amaflags)
+                       tmp->amaflags = i->amaflags;
+               strncpy(tmp->context, i->context, sizeof(tmp->context)-1);
+               strncpy(tmp->exten, i->exten, sizeof(tmp->exten)-1);
+               tmp->adsicpe = i->peeradsicpe;
+               tmp->pvt->fixup = iax_fixup;
                i->owner = tmp;
-               tmp->state = state;
-               pthread_mutex_lock(&usecnt_lock);
+               i->capability = capability;
+               ast_setstate(tmp, state);
+               ast_pthread_mutex_lock(&usecnt_lock);
                usecnt++;
-               pthread_mutex_unlock(&usecnt_lock);
+               ast_pthread_mutex_unlock(&usecnt_lock);
                ast_update_use_count();
                if (state != AST_STATE_DOWN) {
                        if (ast_pbx_start(tmp)) {
@@ -1096,42 +1799,79 @@ static unsigned int calc_timestamp(struct chan_iax_pvt *p, unsigned int ts)
        return ms;
 }
 
+#ifdef BRIDGE_OPTIMIZATION
+static unsigned int calc_fakestamp(struct chan_iax_pvt *p1, struct chan_iax_pvt *p2, unsigned int fakets)
+{
+       int ms;
+       /* Receive from p1, send to p2 */
+       
+       /* Setup rxcore if necessary on outgoing channel */
+       if (!p1->rxcore.tv_sec && !p1->rxcore.tv_usec)
+               gettimeofday(&p1->rxcore, NULL);
+
+       /* Setup txcore if necessary on outgoing channel */
+       if (!p2->offset.tv_sec && !p2->offset.tv_usec)
+               gettimeofday(&p2->offset, NULL);
+       
+       /* Now, ts is the timestamp of the original packet in the orignal context.
+          Adding rxcore to it gives us when we would want the packet to be delivered normally.
+          Subtracting txcore of the outgoing channel gives us what we'd expect */
+       
+       ms = (p1->rxcore.tv_sec - p2->offset.tv_sec) * 1000 + (p1->rxcore.tv_usec - p1->offset.tv_usec) / 1000;
+       fakets += ms;
+       if (fakets <= p2->lastsent)
+               fakets = p2->lastsent + 1;
+       p2->lastsent = fakets;
+       return fakets;
+}
+#endif
+
 static unsigned int calc_rxstamp(struct chan_iax_pvt *p)
 {
        /* Returns where in "receive time" we are */
        struct timeval tv;
        unsigned int ms;
+       /* Setup rxcore if necessary */
        if (!p->rxcore.tv_sec && !p->rxcore.tv_usec)
                gettimeofday(&p->rxcore, NULL);
+
        gettimeofday(&tv, NULL);
-       ms = (tv.tv_sec - p->offset.tv_sec) * 1000 + (tv.tv_usec - p->offset.tv_usec) / 1000;
+       ms = (tv.tv_sec - p->rxcore.tv_sec) * 1000 + (tv.tv_usec - p->rxcore.tv_usec) / 1000;
        return ms;
 }
-static int iax_send(struct chan_iax_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno)
+
+static int iax_send(struct chan_iax_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno, int now, int transfer, int final)
 {
        /* Queue a packet for delivery on a given private structure.  Use "ts" for
-          timestamp, or calculate if ts is 0 */
+          timestamp, or calculate if ts is 0.  Send immediately without retransmission
+          or delayed, with retransmission */
        struct ast_iax_full_hdr *fh;
        struct ast_iax_mini_hdr *mh;
-       struct ast_iax_frame *fr;
+       struct ast_iax_frame *fr, fr2;
        int res;
        unsigned int lastsent;
        /* Allocate an ast_iax_frame */
-       fr = malloc(sizeof(struct ast_iax_frame));
+       if (now)
+               fr = &fr2;
+       else
+               fr = ast_iax_frame_new(DIRECTION_OUTGRESS);
        if (!fr) {
                ast_log(LOG_WARNING, "Out of memory\n");
                return -1;
        }
        if (!pvt) {
                ast_log(LOG_WARNING, "No private structure for packet (%d)?\n", fr->callno);
-               free(fr);
+               if (!now)
+                       ast_iax_frame_free(fr);
                return -1;
        }
        /* Isolate our frame for transmission */
        fr->f = ast_frdup(f);
+
        if (!fr->f) {
                ast_log(LOG_WARNING, "Out of memory\n");
-               free(fr);
+               if (!now)
+                       ast_iax_frame_free(fr);
                return -1;
        }
        if (fr->f->offset < sizeof(struct ast_iax_full_hdr)) {
@@ -1143,9 +1883,13 @@ static int iax_send(struct chan_iax_pvt *pvt, struct ast_frame *f, unsigned int
        fr->ts = calc_timestamp(pvt, ts);
        if (!fr->ts) {
                ast_log(LOG_WARNING, "timestamp is 0?\n");
+               if (!now)
+                       ast_iax_frame_free(fr);
                return -1;
        }
        fr->callno = pvt->callno;
+       fr->transfer = transfer;
+       fr->final = final;
        if (((fr->ts & 0xFFFF0000L) != (lastsent & 0xFFFF0000L))
                /* High two bits of timestamp differ */ ||
            (fr->f->frametype != AST_FRAME_VOICE) 
@@ -1163,11 +1907,10 @@ static int iax_send(struct chan_iax_pvt *pvt, struct ast_frame *f, unsigned int
                fh->seqno = htons(fr->seqno);
                fh->type = fr->f->frametype & 0xFF;
                fh->csub = compress_subclass(fr->f->subclass);
-#if 0
-               fh->subclasshigh = (fr->f->subclass & 0xFF0000) >> 16;
-               fh->subclasslow = htons(fr->f->subclass & 0xFFFF);
-#endif
-               fh->dcallno = htons(pvt->peercallno);
+               if (transfer) {
+                       fh->dcallno = htons(pvt->transfercallno);
+               } else
+                       fh->dcallno = htons(pvt->peercallno);
                fr->datalen = fr->f->datalen + sizeof(struct ast_iax_full_hdr);
                fr->data = fh;
                fr->retries = 0;
@@ -1183,7 +1926,11 @@ static int iax_send(struct chan_iax_pvt *pvt, struct ast_frame *f, unsigned int
                if (f->frametype == AST_FRAME_VOICE) {
                        pvt->svoiceformat = f->subclass;
                }
-               res = iax_transmit(fr);
+               if (now) {
+                       res = send_packet(fr);
+                       ast_frfree(fr->f);
+               } else
+                       res = iax_transmit(fr);
        } else {
                /* Mini-frames have no sequence number */
                fr->seqno = -1;
@@ -1194,7 +1941,11 @@ static int iax_send(struct chan_iax_pvt *pvt, struct ast_frame *f, unsigned int
                fr->datalen = fr->f->datalen + sizeof(struct ast_iax_mini_hdr);
                fr->data = mh;
                fr->retries = -1;
-               res = iax_transmit(fr);
+               if (now) {
+                       res = send_packet(fr);
+                       ast_frfree(fr->f);
+               } else
+                       res = iax_transmit(fr);
        }
        return res;
 }
@@ -1207,37 +1958,86 @@ static int iax_show_users(int fd, int argc, char *argv[])
        struct iax_user *user;
        if (argc != 3) 
                return RESULT_SHOWUSAGE;
-       pthread_mutex_lock(&userl.lock);
+       ast_pthread_mutex_lock(&userl.lock);
        ast_cli(fd, FORMAT, "Username", "Secret", "Authen", "Def.Context", "A/C");
        for(user=userl.users;user;user=user->next) {
                ast_cli(fd, FORMAT, user->name, user->secret, user->methods, 
                                user->contexts ? user->contexts->context : context,
                                user->ha ? "Yes" : "No");
        }
-       pthread_mutex_unlock(&userl.lock);
+       ast_pthread_mutex_unlock(&userl.lock);
        return RESULT_SUCCESS;
 #undef FORMAT
 }
 
 static int iax_show_peers(int fd, int argc, char *argv[])
 {
-#define FORMAT2 "%-15.15s  %-15.15s  %-15.15s  %-15.15s  %s\n"
-#define FORMAT "%-15.15s  %-15.15s  %-15.15s  %-15.15s  %d\n"
+#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"
        struct iax_peer *peer;
        if (argc != 3)
                return RESULT_SHOWUSAGE;
-       pthread_mutex_lock(&peerl.lock);
-       ast_cli(fd, FORMAT2, "Name", "Username", "Host", "Mask", "Port");
+       ast_pthread_mutex_lock(&peerl.lock);
+       ast_cli(fd, FORMAT2, "Name", "Username", "Host", "   ", "Mask", "Port");
        for (peer = peerl.peers;peer;peer = peer->next) {
                char nm[20];
-               strncpy(nm, inet_ntoa(peer->mask), sizeof(nm));
+               strncpy(nm, inet_ntoa(peer->mask), sizeof(nm)-1);
                ast_cli(fd, FORMAT, peer->name, 
                                        peer->username ? peer->username : "(Any)",
-                                       peer->addr.sin_addr.s_addr ? inet_ntoa(peer->addr.sin_addr) : "(Any)",
+                                       peer->addr.sin_addr.s_addr ? inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
+                                       peer->dynamic ? "(D)" : "   ",
                                        nm,
                                        ntohs(peer->addr.sin_port));
        }
-       pthread_mutex_unlock(&peerl.lock);
+       ast_pthread_mutex_unlock(&peerl.lock);
+       return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static char *regstate2str(int regstate)
+{
+       switch(regstate) {
+       case REG_STATE_UNREGISTERED:
+               return "Unregistered";
+       case REG_STATE_REGSENT:
+               return "Request Sent";
+       case REG_STATE_AUTHSENT:
+               return "Auth. Sent";
+       case REG_STATE_REGISTERED:
+               return "Registered";
+       case REG_STATE_REJECTED:
+               return "Rejected";
+       case REG_STATE_TIMEOUT:
+               return "Timeout";
+       case REG_STATE_NOAUTH:
+               return "No Authentication";
+       default:
+               return "Unknown";
+       }
+}
+
+static int iax_show_registry(int fd, int argc, char *argv[])
+{
+#define FORMAT2 "%-20.20s  %-10.10s  %-20.20s %8.8s  %s\n"
+#define FORMAT "%-20.20s  %-10.10s  %-20.20s %8d  %s\n"
+       struct iax_registry *reg;
+       char host[80];
+       char perceived[80];
+       if (argc != 3)
+               return RESULT_SHOWUSAGE;
+       ast_pthread_mutex_lock(&peerl.lock);
+       ast_cli(fd, FORMAT2, "Host", "Username", "Perceived", "Refresh", "State");
+       for (reg = registrations;reg;reg = reg->next) {
+               snprintf(host, sizeof(host), "%s:%d", inet_ntoa(reg->addr.sin_addr), ntohs(reg->addr.sin_port));
+               if (reg->us.sin_addr.s_addr) 
+                       snprintf(perceived, sizeof(perceived), "%s:%d", inet_ntoa(reg->us.sin_addr), ntohs(reg->us.sin_port));
+               else
+                       strcpy(perceived, "<Unregistered>");
+               ast_cli(fd, FORMAT, host, 
+                                       reg->username, perceived, reg->refresh, regstate2str(reg->regstate));
+       }
+       ast_pthread_mutex_unlock(&peerl.lock);
        return RESULT_SUCCESS;
 #undef FORMAT
 #undef FORMAT2
@@ -1250,7 +2050,7 @@ static int iax_show_channels(int fd, int argc, char *argv[])
        int x;
        if (argc != 3)
                return RESULT_SHOWUSAGE;
-       pthread_mutex_lock(&iaxs_lock);
+       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++)
                if (iaxs[x]) 
@@ -1261,12 +2061,32 @@ static int iax_show_channels(int fd, int argc, char *argv[])
                                                iaxs[x]->lag,
                                                iaxs[x]->jitter,
                                                iaxs[x]->voiceformat);
-       pthread_mutex_unlock(&iaxs_lock);
+       ast_pthread_mutex_unlock(&iaxs_lock);
        return RESULT_SUCCESS;
 #undef FORMAT
 #undef FORMAT2
 }
 
+static int iax_do_debug(int fd, int argc, char *argv[])
+{
+       if (argc != 2)
+               return RESULT_SHOWUSAGE;
+       iaxdebug = 1;
+       ast_cli(fd, "IAX Debugging Enabled\n");
+       return RESULT_SUCCESS;
+}
+
+static int iax_no_debug(int fd, int argc, char *argv[])
+{
+       if (argc != 3)
+               return RESULT_SHOWUSAGE;
+       iaxdebug = 0;
+       ast_cli(fd, "IAX Debugging Disabled\n");
+       return RESULT_SUCCESS;
+}
+
+
+
 static char show_users_usage[] = 
 "Usage: iax show users\n"
 "       Lists all users known to the IAX (Inter-Asterisk eXchange) subsystem.\n";
@@ -1279,29 +2099,60 @@ static char show_peers_usage[] =
 "Usage: iax show peers\n"
 "       Lists all known IAX peers.\n";
 
+static char show_reg_usage[] =
+"Usage: iax show registry\n"
+"       Lists all registration requests and status.\n";
+
+#ifdef DEBUG_SUPPORT
+
+static char debug_usage[] = 
+"Usage: iax debug\n"
+"       Enables dumping of IAX packets for debugging purposes\n";
+
+static char no_debug_usage[] = 
+"Usage: iax no debug\n"
+"       Disables dumping of IAX packets for debugging purposes\n";
+
+#endif
+
 static struct ast_cli_entry  cli_show_users = 
        { { "iax", "show", "users", NULL }, iax_show_users, "Show defined IAX users", show_users_usage };
 static struct ast_cli_entry  cli_show_channels =
        { { "iax", "show", "channels", NULL }, iax_show_channels, "Show active IAX channels", show_channels_usage };
 static struct ast_cli_entry  cli_show_peers =
        { { "iax", "show", "peers", NULL }, iax_show_peers, "Show defined IAX peers", show_peers_usage };
+static struct ast_cli_entry  cli_show_registry =
+       { { "iax", "show", "registry", NULL }, iax_show_registry, "Show IAX registration status", show_reg_usage };
+static struct ast_cli_entry  cli_debug =
+       { { "iax", "debug", NULL }, iax_do_debug, "Enable IAX debugging", debug_usage };
+static struct ast_cli_entry  cli_no_debug =
+       { { "iax", "no", "debug", NULL }, iax_no_debug, "Disable IAX debugging", no_debug_usage };
 
 static int iax_write(struct ast_channel *c, struct ast_frame *f)
 {
        struct chan_iax_pvt *i = c->pvt->pvt;
+       if (!i)
+               return -1;
        /* If there's an outstanding error, return failure now */
        if (i->error) {
                ast_log(LOG_DEBUG, "Write error: %s\n", strerror(errno));
                return -1;
        }
+       /* If it's already gone, just return */
+       if (i->alreadygone)
+               return 0;
        /* Don't waste bandwidth sending null frames */
        if (f->frametype == AST_FRAME_NULL)
                return 0;
+       /* If we're quelching voice, don't bother sending it */
+       if ((f->frametype == AST_FRAME_VOICE) && i->quelch)
+               return 0;
        /* Simple, just queue for transmission */
-       return iax_send(i, f, 0, -1);
+       return iax_send(i, f, 0, -1, 0, 0, 0);
 }
 
-static int send_command(struct chan_iax_pvt *i, char type, int command, unsigned int ts, char *data, int datalen, int seqno)
+static int __send_command(struct chan_iax_pvt *i, char type, int command, unsigned int ts, char *data, int datalen, int seqno, 
+               int now, int transfer, int final)
 {
        struct ast_frame f;
        f.frametype = type;
@@ -1312,7 +2163,35 @@ static int send_command(struct chan_iax_pvt *i, char type, int command, unsigned
        f.offset = 0;
        f.src = __FUNCTION__;
        f.data = data;
-       return iax_send(i, &f, ts, seqno);
+       return iax_send(i, &f, ts, seqno, now, transfer, final);
+}
+
+static int send_command(struct chan_iax_pvt *i, char type, int command, unsigned int ts, char *data, int datalen, int seqno)
+{
+       return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 0);
+}
+
+#ifdef BRIDGE_OPTIMIZATION
+static int forward_command(struct chan_iax_pvt *i, char type, int command, unsigned int ts, char *data, int datalen, int seqno)
+{
+       return __send_command(iaxs[i->bridgecallno], type, command, ts, data, datalen, seqno, 0, 0, 0);
+}
+#endif
+
+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);
+       return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 1);
+}
+
+static int send_command_immediate(struct chan_iax_pvt *i, char type, int command, unsigned int ts, char *data, int datalen, int seqno)
+{
+       return __send_command(i, type, command, ts, data, datalen, seqno, 1, 0, 0);
+}
+
+static int send_command_transfer(struct chan_iax_pvt *i, char type, int command, unsigned int ts, char *data, int datalen)
+{
+       return __send_command(i, type, command, ts, data, datalen, 0, 0, 1, 0);
 }
 
 static int apply_context(struct iax_context *con, char *context)
@@ -1325,25 +2204,11 @@ static int apply_context(struct iax_context *con, char *context)
        return 0;
 }
 
-static int apply_ha(struct iax_ha *ha, struct sockaddr_in *sin)
-{
-       /* Start optimistic */
-       int res = IAX_SENSE_ALLOW;
-       while(ha) {
-               /* For each rule, if this address and the netmask = the net address
-                  apply the current rule */
-               if ((sin->sin_addr.s_addr & ha->netmask.s_addr) == (ha->netaddr.s_addr))
-                       res = ha->sense;
-               ha = ha->next;
-       }
-       return res;
-}
-
 static int iax_getformats(int callno, char *orequest)
 {
        char *var, *value;
        char request[256];
-       strncpy(request, orequest, sizeof(request));
+       strncpy(request, orequest, sizeof(request)-1);
        var = strtok(request, ";");
        while(var) {
                value = strchr(var, '=');
@@ -1351,7 +2216,7 @@ static int iax_getformats(int callno, char *orequest)
                        *value='\0';
                        value++;
                        if (!strcmp(var, "formats")) {
-                               iaxs[callno]->peerformats = atoi(value);
+                               iaxs[callno]->peerformat = atoi(value);
                        } else 
                                ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
                }
@@ -1360,6 +2225,7 @@ static int iax_getformats(int callno, char *orequest)
        return 0;
 }
 
+
 static int check_access(int callno, struct sockaddr_in *sin, char *orequest, int requestl)
 {
        /* Start pessimistic */
@@ -1368,7 +2234,8 @@ static int check_access(int callno, struct sockaddr_in *sin, char *orequest, int
        char *var, *value;
        struct iax_user *user;
        char request[256];
-       strncpy(request, orequest, sizeof(request));
+       int gotcapability=0;
+       strncpy(request, orequest, sizeof(request)-1);
        if (!iaxs[callno])
                return res;
        var = strtok(request, ";");
@@ -1378,60 +2245,83 @@ static int check_access(int callno, struct sockaddr_in *sin, char *orequest, int
                        *value='\0';
                        value++;
                        if (!strcmp(var, "exten")) 
-                               strncpy(iaxs[callno]->exten, value, sizeof(iaxs[callno]->exten));
+                               strncpy(iaxs[callno]->exten, value, sizeof(iaxs[callno]->exten)-1);
                        else if (!strcmp(var, "callerid"))
-                               strncpy(iaxs[callno]->callerid, value, sizeof(iaxs[callno]->callerid));
+                               strncpy(iaxs[callno]->callerid, value, sizeof(iaxs[callno]->callerid)-1);
+                       else if (!strcmp(var, "ani"))
+                               strncpy(iaxs[callno]->ani, value, sizeof(iaxs[callno]->ani) - 1);
                        else if (!strcmp(var, "dnid"))
-                               strncpy(iaxs[callno]->dnid, value, sizeof(iaxs[callno]->dnid));
+                               strncpy(iaxs[callno]->dnid, value, sizeof(iaxs[callno]->dnid)-1);
                        else if (!strcmp(var, "context"))
-                               strncpy(iaxs[callno]->context, value, sizeof(iaxs[callno]->context));
+                               strncpy(iaxs[callno]->context, value, sizeof(iaxs[callno]->context)-1);
+                       else if (!strcmp(var, "language"))
+                               strncpy(iaxs[callno]->language, value, sizeof(iaxs[callno]->language)-1);
                        else if (!strcmp(var, "username"))
-                               strncpy(iaxs[callno]->username, value, sizeof(iaxs[callno]->username));
-                       else if (!strcmp(var, "formats"))
-                               iaxs[callno]->peerformats = atoi(value);
-                       else if (!strcmp(var, "version"))
+                               strncpy(iaxs[callno]->username, value, sizeof(iaxs[callno]->username)-1);
+                       else if (!strcmp(var, "formats")) 
+                               iaxs[callno]->peerformat = atoi(value);
+                       else if (!strcmp(var, "adsicpe"))
+                               iaxs[callno]->peeradsicpe = atoi(value);
+                       else if (!strcmp(var, "capability")) {
+                               gotcapability = 1;
+                               iaxs[callno]->peercapability = atoi(value);
+                       } else if (!strcmp(var, "version"))
                                version = atoi(value);
                        else 
                                ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
                }
                var = strtok(NULL, ";");
        }
+       if (!gotcapability) 
+               iaxs[callno]->peercapability = iaxs[callno]->peerformat;
        if (version > AST_IAX_PROTO_VERSION) {
                ast_log(LOG_WARNING, "Peer '%s' has too new a protocol version (%d) for me\n", 
                        inet_ntoa(sin->sin_addr), version);
                return res;
        }
-       pthread_mutex_lock(&userl.lock);
+       ast_pthread_mutex_lock(&userl.lock);
        /* Search the userlist for a compatible entry, and fill in the rest */
        user = userl.users;
        while(user) {
                if ((!strlen(iaxs[callno]->username) ||                         /* No username specified */
                        !strcmp(iaxs[callno]->username, user->name))    /* Or this username specified */
-                       && (apply_ha(user->ha, sin) == IAX_SENSE_ALLOW) /* Access is permitted from this IP */
+                       && ast_apply_ha(user->ha, sin)  /* Access is permitted from this IP */
                        && (!strlen(iaxs[callno]->context) ||                   /* No context specified */
                             apply_context(user->contexts, iaxs[callno]->context))) {                   /* Context is permitted */
                        /* We found our match (use the first) */
                        
                        /* Store the requested username if not specified */
                        if (!strlen(iaxs[callno]->username))
-                               strncpy(iaxs[callno]->username, user->name, sizeof(iaxs[callno]->username));
+                               strncpy(iaxs[callno]->username, user->name, sizeof(iaxs[callno]->username)-1);
                        /* And use the default context */
                        if (!strlen(iaxs[callno]->context)) {
                                if (user->contexts)
-                                       strncpy(iaxs[callno]->context, user->contexts->context, sizeof(iaxs[callno]->context));
+                                       strncpy(iaxs[callno]->context, user->contexts->context, sizeof(iaxs[callno]->context)-1);
                                else
-                                       strncpy(iaxs[callno]->context, context, sizeof(iaxs[callno]->context));
+                                       strncpy(iaxs[callno]->context, context, sizeof(iaxs[callno]->context)-1);
                        }
                        /* Copy the secret */
-                       strncpy(iaxs[callno]->secret, user->secret, sizeof(iaxs[callno]->secret));
+                       strncpy(iaxs[callno]->secret, user->secret, sizeof(iaxs[callno]->secret)-1);
+                       /* And any input keys */
+                       strncpy(iaxs[callno]->inkeys, user->inkeys, sizeof(iaxs[callno]->inkeys));
                        /* And the permitted authentication methods */
-                       strncpy(iaxs[callno]->methods, user->methods, sizeof(iaxs[callno]->methods));
+                       strncpy(iaxs[callno]->methods, user->methods, sizeof(iaxs[callno]->methods)-1);
+                       /* If they have callerid, override the given caller id.  Always store the ANI */
+                       if (strlen(iaxs[callno]->callerid)) {
+                               if (user->hascallerid)
+                                       strncpy(iaxs[callno]->callerid, user->callerid, sizeof(iaxs[callno]->callerid)-1);
+                               strncpy(iaxs[callno]->ani, user->callerid, sizeof(iaxs[callno]->ani)-1);
+                       }
+                       if (strlen(user->accountcode))
+                               strncpy(iaxs[callno]->accountcode, user->accountcode, sizeof(iaxs[callno]->accountcode)-1);
+                       if (user->amaflags)
+                               iaxs[callno]->amaflags = user->amaflags;
                        res = 0;
                        break;
                }
                user = user->next;      
        }
-       pthread_mutex_unlock(&userl.lock);
+       ast_pthread_mutex_unlock(&userl.lock);
        return res;
 }
 
@@ -1444,18 +2334,20 @@ static int raw_hangup(struct sockaddr_in *sin, short src, short dst)
        fh.seqno = 0;
        fh.type = AST_FRAME_IAX;
        fh.csub = compress_subclass(AST_IAX_COMMAND_INVAL);
+#if 0
        if (option_debug)
-               ast_log(LOG_DEBUG, "Raw Hangup\n");
-       return sendto(netsocket, &fh, sizeof(fh), 0, sin, sizeof(*sin));
+#endif 
+               ast_log(LOG_DEBUG, "Raw Hangup %s:%d, src=%d, dst=%d\n",
+                       inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), src, dst);
+       return sendto(netsocket, &fh, sizeof(fh), 0, (struct sockaddr *)sin, sizeof(*sin));
 }
 
 static int authenticate_request(struct chan_iax_pvt *p)
 {
        char requeststr[256] = "";
        MYSNPRINTF "methods=%s;", p->methods);
-       if (strstr(p->methods, "md5")) {
+       if (strstr(p->methods, "md5") || strstr(p->methods, "rsa")) {
                /* Build the challenge */
-               srand(time(NULL));
                snprintf(p->challenge, sizeof(p->challenge), "%d", rand());
                MYSNPRINTF "challenge=%s;", p->challenge);
        }
@@ -1471,12 +2363,13 @@ static int authenticate_verify(struct chan_iax_pvt *p, char *orequest)
        char *var, *value, request[256];
        char md5secret[256] = "";
        char secret[256] = "";
+       char rsasecret[256] = "";
        int res = -1; 
        int x;
        
        if (!(p->state & IAX_STATE_AUTHENTICATED))
                return res;
-       strncpy(request, orequest, sizeof(request));
+       strncpy(request, orequest, sizeof(request)-1);
        var = strtok(request, ";");
        while(var) {
                value = strchr(var, '=');
@@ -1484,15 +2377,32 @@ static int authenticate_verify(struct chan_iax_pvt *p, char *orequest)
                        *value='\0';
                        value++;
                        if (!strcmp(var, "secret")) 
-                               strncpy(secret, value, sizeof(secret));
+                               strncpy(secret, value, sizeof(secret)-1);
                        else if (!strcmp(var, "md5secret"))
-                               strncpy(md5secret, value, sizeof(md5secret));
-                       else 
+                               strncpy(md5secret, value, sizeof(md5secret)-1);
+                       else if (!strcmp(var, "rsasecret"))
+                               strncpy(rsasecret, value, sizeof(rsasecret)-1);
+                       else
                                ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
                }
                var = strtok(NULL, ";");
        }
-       if (strstr(p->methods, "md5")) {
+       if (strstr(p->methods, "rsa") && strlen(rsasecret) && strlen(p->inkeys)) {
+               struct ast_key *key;
+               char *keyn;
+               char tmpkey[256];
+               strncpy(tmpkey, p->inkeys, sizeof(tmpkey));
+               keyn = strtok(tmpkey, ":");
+               while(keyn) {
+                       key = ast_key_get(keyn, AST_KEY_PUBLIC);
+                       if (key && !ast_check_signature(key, p->challenge, rsasecret)) {
+                               res = 0;
+                               break;
+                       } else if (!key)
+                               ast_log(LOG_WARNING, "requested inkey '%s' for RSA authentication does not exist\n", keyn);
+                       keyn = strtok(NULL, ":");
+               }
+       } else if (strstr(p->methods, "md5")) {
                struct MD5Context md5;
                unsigned char digest[16];
                MD5Init(&md5);
@@ -1511,7 +2421,182 @@ static int authenticate_verify(struct chan_iax_pvt *p, char *orequest)
        return res;
 }
 
-static int authenticate_reply(struct chan_iax_pvt *p, struct sockaddr_in *sin, char *orequest)
+static int register_verify(int callno, struct sockaddr_in *sin, char *orequest)
+{
+       char request[256];
+       char requeststr[256] = "";
+       char peer[256] = "";
+       char md5secret[256] = "";
+       char rsasecret[256] = "";
+       char secret[256] = "";
+       struct iax_peer *p;
+       struct ast_key *key;
+       char *var;
+       char *value;
+       char *keyn;
+       int x;
+       int expire = 0;
+
+       iaxs[callno]->state &= ~IAX_STATE_AUTHENTICATED;
+       strcpy(iaxs[callno]->peer, "");
+       if (!orequest)
+               return -1;
+       strncpy(request, orequest, sizeof(request)-1);
+       var = strtok(request, ";");
+       while(var) {
+               value = strchr(var, '=');
+               if (value) { 
+                       *value='\0';
+                       value++;
+                       if (!strcmp(var, "peer")) 
+                               strncpy(peer, value, sizeof(peer)-1);
+                       else if (!strcmp(var, "md5secret"))
+                               strncpy(md5secret, value, sizeof(md5secret)-1);
+                       else if (!strcmp(var, "rsasecret"))
+                               strncpy(rsasecret, value, sizeof(rsasecret)-1);
+                       else if (!strcmp(var, "secret"))
+                               strncpy(secret, value, sizeof(secret)-1);
+                       else if (!strcmp(var, "refresh"))
+                               expire = atoi(value);
+                       else 
+                               ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
+               }
+               var = strtok(NULL, ";");
+       }
+
+       if (!strlen(peer)) {
+               ast_log(LOG_NOTICE, "Empty registration from %s\n", inet_ntoa(sin->sin_addr));
+               return -1;
+       }
+
+       for (p = peerl.peers; p ; p = p->next) 
+               if (!strcasecmp(p->name, peer))
+                       break;
+
+       if (!p) {
+               ast_log(LOG_NOTICE, "No registration for peer '%s' (from %s)\n", peer, inet_ntoa(sin->sin_addr));
+               return -1;
+       }
+
+       if (!p->dynamic) {
+               ast_log(LOG_NOTICE, "Peer '%s' is not dynamic (from %s)\n", peer, inet_ntoa(sin->sin_addr));
+               return -1;
+       }
+
+       if (!ast_apply_ha(p->ha, sin)) {
+               ast_log(LOG_NOTICE, "Host %s denied access to register peer '%s'\n", inet_ntoa(sin->sin_addr), p->name);
+               return -1;
+       }
+       strncpy(iaxs[callno]->secret, p->secret, sizeof(iaxs[callno]->secret)-1);
+       strncpy(iaxs[callno]->inkeys, p->inkeys, sizeof(iaxs[callno]->inkeys)-1);
+       /* Check secret against what we have on file */
+       if (strlen(rsasecret) && strstr(p->methods, "rsa") && strlen(p->challenge)) {
+               if (strlen(p->inkeys)) {
+                       char tmpkeys[256];
+                       strncpy(tmpkeys, p->inkeys, sizeof(tmpkeys));
+                       keyn = strtok(tmpkeys, ":");
+                       while(keyn) {
+                               key = ast_key_get(keyn, AST_KEY_PUBLIC);
+                               if (key && !ast_check_signature(key, p->challenge, rsasecret)) {
+                                       iaxs[callno]->state |= IAX_STATE_AUTHENTICATED;
+                                       break;
+                               } else if (!key) 
+                                       ast_log(LOG_WARNING, "requested inkey '%s' does not exist\n", keyn);
+                               keyn = strtok(NULL, ":");
+                       }
+                       if (!keyn) {
+                               ast_log(LOG_NOTICE, "Host %s failed RSA authentication with inkeys '%s'\n", peer, p->inkeys);
+                               return -1;
+                       }
+               } else {
+                       ast_log(LOG_NOTICE, "Host '%s' trying to do RSA authentication, but we have no inkeys\n", peer);
+                       return -1;
+               }
+       } else if (strlen(secret) && strstr(p->methods, "plaintext")) {
+               /* They've provided a plain text password and we support that */
+               if (strcmp(secret, p->secret)) {
+                       ast_log(LOG_NOTICE, "Host %s did not provide proper plaintext password for '%s'\n", inet_ntoa(sin->sin_addr), p->name);
+                       return -1;
+               } else
+                       iaxs[callno]->state |= IAX_STATE_AUTHENTICATED;
+       } else if (strlen(md5secret) && strstr(p->methods, "md5") && strlen(p->challenge)) {
+               struct MD5Context md5;
+               unsigned char digest[16];
+               MD5Init(&md5);
+               MD5Update(&md5, p->challenge, strlen(p->challenge));
+               MD5Update(&md5, p->secret, strlen(p->secret));
+               MD5Final(digest, &md5);
+               for (x=0;x<16;x++)
+                       MYSNPRINTF "%2.2x", digest[x]);
+               if (strcasecmp(requeststr, md5secret)) {
+                       ast_log(LOG_NOTICE, "Host %s failed MD5 authentication for '%s' (%s != %s)\n", inet_ntoa(sin->sin_addr), p->name, requeststr, md5secret);
+                       return -1;
+               } else
+                       iaxs[callno]->state |= IAX_STATE_AUTHENTICATED;
+       } else if (strlen(md5secret) || strlen(secret)) {
+               ast_log(LOG_NOTICE, "Inappropriate authentication received\n");
+               return -1;
+       }
+       strncpy(iaxs[callno]->peer, peer, sizeof(iaxs[callno]->peer)-1);
+       /* Choose lowest expirey number */
+       if (expire && (expire < iaxs[callno]->expirey)) 
+               iaxs[callno]->expirey = expire;
+       return 0;
+       
+}
+
+static int authenticate(char *challenge, char *secret, char *keyn, char *methods, char *requeststr, int reqsize, struct sockaddr_in *sin)
+{
+       int res = -1;
+       int x;
+       if (keyn && strlen(keyn)) {
+               if (!strstr(methods, "rsa")) {
+                       if (!secret || !strlen(secret)) 
+                               ast_log(LOG_NOTICE, "Asked to authenticate to %s with an RSA key, but they don't allow RSA authentication\n", inet_ntoa(sin->sin_addr));
+               } else if (!strlen(challenge)) {
+                       ast_log(LOG_NOTICE, "No challenge provided for RSA authentication to %s\n", inet_ntoa(sin->sin_addr));
+               } else {
+                       char sig[256];
+                       struct ast_key *key;
+                       key = ast_key_get(keyn, AST_KEY_PRIVATE);
+                       if (!key) {
+                               ast_log(LOG_NOTICE, "Unable to find private key '%s'\n", keyn);
+                       } else {
+                               if (ast_sign(key, challenge, sig)) {
+                                       ast_log(LOG_NOTICE, "Unable to sign challenge withy key\n");
+                                       res = -1;
+                               } else {
+                                       MYSNPRINTF2 "rsasecret=%s;", sig);
+                                       res = 0;
+                               }
+                       }
+               }
+       } 
+       /* Fall back */
+       if (res && secret && strlen(secret)) {
+               if (strstr(methods, "md5") && strlen(challenge)) {
+                       struct MD5Context md5;
+                       unsigned char digest[16];
+                       MD5Init(&md5);
+                       MD5Update(&md5, challenge, strlen(challenge));
+                       MD5Update(&md5, secret, strlen(secret));
+                       MD5Final(digest, &md5);
+                       /* If they support md5, authenticate with it.  */
+                       MYSNPRINTF2 "md5secret=");
+                       for (x=0;x<16;x++)
+                               MYSNPRINTF2 "%2.2x", digest[x]);
+                       MYSNPRINTF2 ";");
+                       res = 0;
+               } else if (strstr(methods, "plaintext")) {
+                       MYSNPRINTF2 "secret=%s;", secret);
+                       res = 0;
+               } else
+                       ast_log(LOG_NOTICE, "No way to send secret to peer '%s' (their methods: %s)\n", inet_ntoa(sin->sin_addr), methods);
+       }
+       return res;
+}
+
+static int authenticate_reply(struct chan_iax_pvt *p, struct sockaddr_in *sin, char *orequest, char *override, char *okey)
 {
        struct iax_peer *peer;
        /* Start pessimistic */
@@ -1520,8 +2605,8 @@ static int authenticate_reply(struct chan_iax_pvt *p, struct sockaddr_in *sin, c
        char methods[80] = "";
        char requeststr[256] = "";
        char *var, *value;
-       int x;
-       strncpy(request, orequest, sizeof(request));
+       
+       strncpy(request, orequest, sizeof(request)-1);
        var = strtok(request, ";");
        while(var) {
                value = strchr(var, '=');
@@ -1529,80 +2614,570 @@ static int authenticate_reply(struct chan_iax_pvt *p, struct sockaddr_in *sin, c
                        *value='\0';
                        value++;
                        if (!strcmp(var, "username")) 
-                               strncpy(p->username, value, sizeof(p->username));
+                               strncpy(p->username, value, sizeof(p->username)-1);
                        else if (!strcmp(var, "challenge"))
-                               strncpy(p->challenge, value, sizeof(p->challenge));
+                               strncpy(p->challenge, value, sizeof(p->challenge)-1);
                        else if (!strcmp(var, "methods"))
-                               strncpy(methods, value, sizeof(methods));
+                               strncpy(methods, value, sizeof(methods)-1);
                        else 
                                ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
                }
                var = strtok(NULL, ";");
        }
-       pthread_mutex_lock(&peerl.lock);
-       peer = peerl.peers;
-       while(peer) {
-               if ((!strlen(p->peer) || !strcmp(p->peer, peer->name)) 
+
+       /* Check for override RSA authentication first */
+       if ((override && strlen(override)) || (okey && strlen(okey))) {
+               /* Normal password authentication */
+               res = authenticate(p->challenge, override, okey, methods, requeststr, sizeof(requeststr), sin);
+       } else {
+               ast_pthread_mutex_lock(&peerl.lock);
+               peer = peerl.peers;
+               while(peer) {
+                       if ((!strlen(p->peer) || !strcmp(p->peer, peer->name)) 
                                                                /* No peer specified at our end, or this is the peer */
                         && (!strlen(peer->username) || (!strcmp(peer->username, p->username)))
                                                                /* No username specified in peer rule, or this is the right username */
                         && (!peer->addr.sin_addr.s_addr || ((sin->sin_addr.s_addr & peer->mask.s_addr) == (peer->addr.sin_addr.s_addr & peer->mask.s_addr)))
                                                                /* No specified host, or this is our host */
                        ) {
-                       /* We have a match, authenticate it. */
-                       res = 0;
-                       if (strstr(methods, "md5")) {
-                               struct MD5Context md5;
-                               unsigned char digest[16];
-                               MD5Init(&md5);
-                               MD5Update(&md5, p->challenge, strlen(p->challenge));
-                               MD5Update(&md5, peer->secret, strlen(peer->secret));
-                               MD5Final(digest, &md5);
-                               /* If they support md5, authenticate with it.  */
-                               MYSNPRINTF "md5secret=");
-                               for (x=0;x<16;x++)
-                                       MYSNPRINTF "%2.2x", digest[x]);
-                               MYSNPRINTF ";");
-                       } else if (strstr(methods, "plaintext")) {
-                               MYSNPRINTF "secret=%s;", peer->secret);
-                       } else 
-                               res = -1;
-                       if (strlen(requeststr))
-                               requeststr[strlen(requeststr)-1] = '\0';
-                       if (!res)
-                               res = send_command(p, AST_FRAME_IAX, AST_IAX_COMMAND_AUTHREP, 0, requeststr, strlen(requeststr) + 1, -1);
-                       break;  
+                               res = authenticate(p->challenge, peer->secret, peer->outkey, methods, requeststr, sizeof(requeststr), sin);
+                               if (!res)
+                                       break;  
+                       }
+                       peer = peer->next;
                }
-               peer = peer->next;
+               ast_pthread_mutex_unlock(&peerl.lock);
        }
-       pthread_mutex_unlock(&peerl.lock);
+       if (strlen(requeststr))
+               requeststr[strlen(requeststr)-1] = '\0';
+       if (!res)
+               res = send_command(p, AST_FRAME_IAX, AST_IAX_COMMAND_AUTHREP, 0, requeststr, strlen(requeststr) + 1, -1);
        return res;
 }
 
-static int socket_read(int *id, int fd, short events, void *cbdata)
+static int iax_do_register(struct iax_registry *reg);
+
+static int iax_do_register_s(void *data)
 {
-       struct sockaddr_in sin;
-       int res;
-       int new = NEW_PREVENT;
-       char buf[4096];
-       char src[80];
-       int len = sizeof(sin);
-       int dcallno = -1;
-       struct ast_iax_full_hdr *fh = (struct ast_iax_full_hdr *)buf;
-       struct ast_iax_mini_hdr *mh = (struct ast_iax_mini_hdr *)buf;
-       struct ast_iax_frame fr, *cur;
-       struct ast_frame f;
-       struct ast_channel *c;
-       res = recvfrom(netsocket, buf, sizeof(buf), 0, &sin, &len);
-       if (res < 0) {
-               ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
-               handle_error();
-               return 1;
-       }
-       if (res < sizeof(struct ast_iax_mini_hdr)) {
-               ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, sizeof(struct ast_iax_mini_hdr));
-               return 1;
-       }
+       struct iax_registry *reg = data;
+       reg->expire = -1;
+       iax_do_register(reg);
+       return 0;
+}
+
+static int try_transfer(struct chan_iax_pvt *pvt, char *orequest)
+{
+       int newport = 0;
+       int newcall = 0;
+       char newip[256] = "";
+       char request[256] = "";
+       char *var, *value;
+       
+       struct sockaddr_in new;
+       
+       if (!orequest)
+               return -1;
+
+       strncpy(request, orequest, sizeof(request)-1);
+       var = strtok(request, ";");
+       while(var) {
+               value = strchr(var, '=');
+               if (value) { 
+                       *value='\0';
+                       value++;
+                       if (!strcmp(var, "remip")) 
+                               strncpy(newip, value, sizeof(newip)-1);
+                       else if (!strcmp(var, "remport"))
+                               newport = atoi(value);
+                       else if (!strcmp(var, "remcall"))
+                               newcall = atoi(value);
+                       else 
+                               ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
+               }
+               var = strtok(NULL, ";");
+       }
+       if (!newcall || !inet_aton(newip, &new.sin_addr) || !newport) {
+               ast_log(LOG_WARNING, "Invalid transfer request\n");
+               return -1;
+       }
+       pvt->transfercallno = newcall;
+       inet_aton(newip, &pvt->transfer.sin_addr);
+       pvt->transfer.sin_port = htons(newport);
+       pvt->transfer.sin_family = AF_INET;
+       pvt->transferring = TRANSFER_BEGIN;
+       send_command_transfer(pvt, AST_FRAME_IAX, AST_IAX_COMMAND_TXCNT, 0, NULL, 0);
+       return 0; 
+}
+
+static int complete_dpreply(struct chan_iax_pvt *pvt, char *orequest)
+{
+       char *var, *value;
+       char request[256] = "";
+       char exten[256] = "";
+       int status = CACHE_FLAG_UNKNOWN;
+       int expirey = iaxdefaultdpcache;
+       int x;
+       struct iax_dpcache *dp, *prev;
+       
+       strncpy(request, orequest, sizeof(request)-1);
+       var = strtok(request, ";");
+       while(var) {
+               value = strchr(var, '=');
+               if (value) { 
+                       *value='\0';
+                       value++;
+                       if (!strcmp(var, "number"))
+                               strncpy(exten, value, sizeof(exten)-1);
+                       else if (!strcmp(var, "status")) {
+                               if (!strcasecmp(value, "exists"))
+                                       status = CACHE_FLAG_EXISTS;
+                               else if (!strcasecmp(value, "nonexistant"))
+                                       status = CACHE_FLAG_NONEXISTANT;
+                               else if (!strcasecmp(value, "canexist"))
+                                       status = CACHE_FLAG_CANEXIST;
+                               else 
+                                       ast_log(LOG_WARNING, "Unknown status '%s'\n", value);
+                       } else if (!strcmp(var, "expirey"))
+                               expirey = atoi(value);
+                       else if (!strcmp(var, "ignorepat")) {
+                               /* Don' really do much with it */
+                       } else 
+                               ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
+               }
+               var = strtok(NULL, ";");
+       }
+       ast_pthread_mutex_lock(&dpcache_lock);
+       prev = NULL;
+       dp = pvt->dpentries;
+       while(dp) {
+               if (!strcmp(dp->exten, exten)) {
+                       /* Let them go */
+                       if (prev)
+                               prev->peer = dp->peer;
+                       else
+                               pvt->dpentries = dp->peer;
+                       dp->peer = NULL;
+                       dp->callno = -1;
+                       dp->expirey.tv_sec = dp->orig.tv_sec + expirey;
+                       if (dp->flags & CACHE_FLAG_PENDING) {
+                               dp->flags &= ~CACHE_FLAG_PENDING;
+                               dp->flags |= status;
+                       }
+                       /* Wake up waiters */
+                       for (x=0;x<sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++)
+                               if (dp->waiters[x] > -1)
+                                       write(dp->waiters[x], "asdf", 4);
+               }
+               prev = dp;
+               dp = dp->peer;
+       }
+       ast_pthread_mutex_unlock(&dpcache_lock);
+       return 0;
+}
+
+static int complete_transfer(int callno, char *orequest)
+{
+       int peercallno = -1;
+       char request[256] = "";
+       char *var, *value;
+       struct chan_iax_pvt *pvt = iaxs[callno];
+       struct ast_iax_frame *cur;
+       if (!orequest)
+               return -1;
+
+       strncpy(request, orequest, sizeof(request)-1);
+       var = strtok(request, ";");
+       while(var) {
+               value = strchr(var, '=');
+               if (value) { 
+                       *value='\0';
+                       value++;
+                       if (!strcmp(var, "peercallno"))
+                               peercallno = atoi(value);
+                       else 
+                               ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
+               }
+               var = strtok(NULL, ";");
+       }
+       if (peercallno < 0) {
+               ast_log(LOG_WARNING, "Invalid transfer request\n");
+               return -1;
+       }
+       memcpy(&pvt->addr, &pvt->transfer, sizeof(pvt->addr));
+       memset(&pvt->transfer, 0, sizeof(pvt->transfer));
+       /* Reset sequence numbers */
+       pvt->oseqno = 0;
+       pvt->iseqno = 0;
+       pvt->peercallno = peercallno;
+       pvt->transferring = TRANSFER_NONE;
+       pvt->svoiceformat = -1;
+       pvt->voiceformat = 0;
+       pvt->transfercallno = -1;
+       memset(&pvt->rxcore, 0, sizeof(pvt->rxcore));
+       memset(&pvt->offset, 0, sizeof(pvt->offset));
+       memset(&pvt->history, 0, sizeof(pvt->history));
+       pvt->jitterbuffer = 0;
+       pvt->jitter = 0;
+       pvt->historicjitter = 0;
+       pvt->lag = 0;
+       pvt->last = 0;
+       pvt->lastsent = 0;
+       pvt->pingtime = DEFAULT_RETRY_TIME;
+       ast_pthread_mutex_lock(&iaxq.lock);
+       for (cur = iaxq.head; cur ; cur = cur->next) {
+               /* We must cancel any packets that would have been transmitted
+                  because now we're talking to someone new.  It's okay, they
+                  were transmitted to someone that didn't care anyway. */
+               if (callno == cur->callno) 
+                       cur->retries = -1;
+       }
+       ast_pthread_mutex_unlock(&iaxq.lock);
+       return 0; 
+}
+
+static int iax_ack_registry(char *orequest, struct sockaddr_in *sin)
+{
+       struct iax_registry *reg;
+       /* Start pessimistic */
+       char request[256] = "";
+       char peer[256] = "";
+       int ourport = 0;
+       int refresh = 0;
+       char ourip[256] = "<Unspecified>";
+       struct sockaddr_in oldus;
+       char *var, *value;
+
+       if (!orequest)
+               return -1;
+
+       strncpy(request, orequest, sizeof(request)-1);
+       var = strtok(request, ";");
+       while(var) {
+               value = strchr(var, '=');
+               if (value) { 
+                       *value='\0';
+                       value++;
+                       if (!strcmp(var, "yourip")) 
+                               strncpy(ourip, value, sizeof(ourip)-1);
+                       else if (!strcmp(var, "peer"))
+                               strncpy(peer, value, sizeof(peer)-1);
+                       else if (!strcmp(var, "yourport"))
+                               ourport = atoi(value);
+                       else if (!strcmp(var, "refresh"))
+                               refresh = atoi(value);
+                       else if (!strcmp(var, "callerid")) {
+                               /* We don't really care about suggested Caller*ID, that's more for phones */
+                       } else 
+                               ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
+               }
+               var = strtok(NULL, ";");
+       }
+       reg = registrations;
+       while(reg) {
+               if (!strcasecmp(reg->username, peer)) {
+                       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));
+                               return -1;
+                       }
+                       if (!inet_aton(ourip, &reg->us.sin_addr)) {
+                               ast_log(LOG_WARNING, "Registry ack from '%s' contains invalid IP '%s'\n", inet_ntoa(sin->sin_addr), ourip);
+                               return -1;
+                       }
+                       reg->us.sin_port = htons(ourport);
+                       if (refresh && (reg->refresh < refresh)) {
+                               /* Refresh faster if necessary */
+                               reg->refresh = refresh;
+                               if (reg->expire > -1)
+                                       ast_sched_del(sched, reg->expire);
+                               reg->expire = ast_sched_add(sched, (5 * reg->refresh / 6) * 1000, iax_do_register_s, reg);
+                       }
+                       if (memcmp(&oldus, &reg->us, sizeof(oldus)) && (option_verbose > 2)) {
+                               snprintf(ourip, sizeof(ourip), "%s:%d", inet_ntoa(reg->us.sin_addr), ntohs(reg->us.sin_port));
+                               ast_verbose(VERBOSE_PREFIX_3 "Registered to '%s', who sees us as %s\n", inet_ntoa(sin->sin_addr), ourip);
+                       }
+                       reg->regstate = REG_STATE_REGISTERED;
+                       return 0;
+               }
+               reg = reg->next;
+       }
+       ast_log(LOG_WARNING, "Registry acknowledge on unknown registery '%s'\n", peer);
+       return -1;
+}
+
+static int iax_register(char *value, int lineno)
+{
+       struct iax_registry *reg;
+       char copy[256];
+       char *username, *hostname, *secret;
+       char *porta;
+       
+       struct hostent *hp;
+       if (!value)
+               return -1;
+       strncpy(copy, value, sizeof(copy)-1);
+       username = strtok(copy, "@");
+       hostname = strtok(NULL, "@");
+       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, ";");
+       
+       if (porta && !atoi(porta)) {
+               ast_log(LOG_WARNING, "%s is not a valid port number at line %d\n", porta, lineno);
+               return -1;
+       }
+       hp = gethostbyname(hostname);
+       if (!hp) {
+               ast_log(LOG_WARNING, "Host '%s' not found at line %d\n", hostname, lineno);
+               return -1;
+       }
+       reg = malloc(sizeof(struct iax_registry));
+       if (reg) {
+               memset(reg, 0, sizeof(struct iax_registry));
+               strncpy(reg->username, username, sizeof(reg->username)-1);
+               if (secret)
+                       strncpy(reg->secret, secret, sizeof(reg->secret)-1);
+               reg->expire = -1;
+               reg->refresh = AST_DEFAULT_REG_EXPIRE;
+               reg->addr.sin_family = AF_INET;
+               memcpy(&reg->addr.sin_addr, hp->h_addr, sizeof(&reg->addr.sin_addr));
+               reg->addr.sin_port = porta ? htons(atoi(porta)) : htons(AST_DEFAULT_IAX_PORTNO);
+               reg->next = registrations;
+               reg->callno = -1;
+               registrations = reg;
+       } else {
+               ast_log(LOG_ERROR, "Out of memory\n");
+               return -1;
+       }
+       return 0;
+}
+
+static int expire_registry(void *data)
+{
+       struct iax_peer *p = data;
+       /* Reset the address */
+       memset(&p->addr, 0, sizeof(p->addr));
+       /* Reset expire notice */
+       p->expire = -1;
+       /* Reset expirey value */
+       p->expirey = expirey;
+       if (regfunk)
+               regfunk(p->name, 0);
+       return 0;
+}
+
+static int update_registry(char *name, struct sockaddr_in *sin, int callno)
+{
+       char requeststr[256] = "";
+       struct iax_peer *p;
+       for (p = peerl.peers;p;p = p->next) {
+               if (!strcasecmp(name, p->name)) {
+                       if (memcmp(&p->addr, sin, sizeof(p->addr))) {
+                               if (regfunk)
+                                       regfunk(p->name, 1);
+                               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));
+                       }               
+                       /* Update the host */
+                       memcpy(&p->addr, sin, sizeof(p->addr));
+                       /* Setup the expirey */
+                       if (p->expire > -1)
+                               ast_sched_del(sched, p->expire);
+                       p->expire = ast_sched_add(sched, p->expirey * 1000, expire_registry, (void *)p);
+                       MYSNPRINTF "peer=%s;yourip=%s;yourport=%d;refresh=%d;",
+                               p->name, inet_ntoa(p->addr.sin_addr), ntohs(p->addr.sin_port), p->expirey);
+                       if (p->hascallerid)
+                               MYSNPRINTF "callerid=%s;", p->callerid);
+                       requeststr[strlen(requeststr)-1] = '\0';
+                       return send_command_final(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_REGACK, 0, requeststr, strlen(requeststr) + 1, -1);;
+               }
+       }
+       ast_log(LOG_WARNING, "No such peer '%s'\n", name);
+       return -1;
+}
+
+static int registry_authrequest(char *name, int callno)
+{
+       char requeststr[256] = "";
+       struct iax_peer *p;
+       for (p = peerl.peers;p;p = p->next) {
+               if (!strcasecmp(name, p->name)) {
+                       MYSNPRINTF "methods=%s;", p->methods);
+                       if (strstr(p->methods, "md5") || strstr(p->methods, "rsa")) {
+                               /* Build the challenge */
+                               snprintf(p->challenge, sizeof(p->challenge), "%d", rand());
+                               MYSNPRINTF "challenge=%s;", p->challenge);
+                       }
+                       MYSNPRINTF "peer=%s;", name);
+                       requeststr[strlen(requeststr)-1] = '\0';
+                       return send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_REGAUTH, 0, requeststr, strlen(requeststr) + 1, -1);;
+               }
+       }
+       ast_log(LOG_WARNING, "No such peer '%s'\n", name);
+       return 0;
+}
+
+static int registry_rerequest(char *orequest, int callno, struct sockaddr_in *sin)
+{
+       struct iax_registry *reg;
+       /* Start pessimistic */
+       char request[256] = "";
+       char requeststr[256] = "";
+       char peer[256] = "";
+       char methods[256] = "";
+       char challenge[256] = "";
+       char *var, *value;
+       int res;
+
+       if (!orequest)
+               return -1;
+
+       strncpy(request, orequest, sizeof(request)-1);
+       var = strtok(request, ";");
+       while(var) {
+               value = strchr(var, '=');
+               if (value) { 
+                       *value='\0';
+                       value++;
+                       if (!strcmp(var, "methods")) 
+                               strncpy(methods, value, sizeof(methods)-1);
+                       else if (!strcmp(var, "peer"))
+                               strncpy(peer, value, sizeof(peer)-1);
+                       else if (!strcmp(var, "challenge"))
+                               strncpy(challenge, value, sizeof(challenge)-1);
+                       else 
+                               ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
+               }
+               var = strtok(NULL, ";");
+       }
+       reg = registrations;
+       while(reg) {
+               if (!strcasecmp(reg->username, peer)) {
+                       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;
+                       }
+                       if (!strlen(reg->secret)) {
+                               ast_log(LOG_NOTICE, "No secret associated with peer '%s'\n", reg->username);
+                               reg->regstate = REG_STATE_NOAUTH;
+                               return -1;
+                       }
+                       MYSNPRINTF "peer=%s;refresh=%d;", reg->username, reg->refresh);
+                       if (reg->secret[0] == '[') {
+                               char tmpkey[256];
+                               strncpy(tmpkey, reg->secret + 1, sizeof(tmpkey) - 1);
+                               tmpkey[strlen(tmpkey) - 1] = '\0';
+                               res = authenticate(challenge, NULL, tmpkey, methods, requeststr, sizeof(requeststr), sin);
+                       } else
+                               res = authenticate(challenge, reg->secret, NULL, methods, requeststr, sizeof(requeststr), sin);
+                       if (!res) {
+                               reg->regstate = REG_STATE_AUTHSENT;
+                               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;
+}
+
+static int stop_stuff(int callno)
+{
+               if (iaxs[callno]->lagid > -1)
+                       ast_sched_del(sched, iaxs[callno]->lagid);
+               iaxs[callno]->lagid = -1;
+               if (iaxs[callno]->pingid > -1)
+                       ast_sched_del(sched, iaxs[callno]->pingid);
+               iaxs[callno]->pingid = -1;
+               if (iaxs[callno]->autoid > -1)
+                       ast_sched_del(sched, iaxs[callno]->autoid);
+               iaxs[callno]->autoid = -1;
+               return 0;
+}
+
+static int auto_hangup(void *nothing)
+{
+       int callno = (int)(long)(nothing);
+       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);
+       }
+       return 0;
+}
+
+static void iax_dprequest(struct iax_dpcache *dp, int callno)
+{
+       /* Auto-hangup with 30 seconds of inactivity */
+       if (iaxs[callno]->autoid > -1)
+               ast_sched_del(sched, iaxs[callno]->autoid);
+       iaxs[callno]->autoid = ast_sched_add(sched, 30000, auto_hangup, (void *)callno);
+       send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_DPREQ, 0, dp->exten, strlen(dp->exten) + 1, -1);
+       dp->flags |= CACHE_FLAG_TRANSMITTED;
+}
+
+static int iax_vnak(int callno)
+{
+       return send_command_immediate(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_VNAK, 0, NULL, 0, iaxs[callno]->iseqno);
+}
+
+static void vnak_retransmit(int callno, int last)
+{
+       struct ast_iax_frame *f;
+       ast_pthread_mutex_lock(&iaxq.lock);
+       f = iaxq.head;
+       while(f) {
+               /* Send a copy immediately */
+               if ((f->callno == callno) && iaxs[f->callno] &&
+                       (f->seqno >= last)) {
+                       send_packet(f);
+               }
+               f = f->next;
+       }
+       ast_pthread_mutex_unlock(&iaxq.lock);
+}
+
+static int socket_read(int *id, int fd, short events, void *cbdata)
+{
+       struct sockaddr_in sin;
+       int res;
+       int new = NEW_PREVENT;
+       char buf[4096];
+       char src[80];
+       int len = sizeof(sin);
+       int dcallno = -1;
+       struct ast_iax_full_hdr *fh = (struct ast_iax_full_hdr *)buf;
+       struct ast_iax_mini_hdr *mh = (struct ast_iax_mini_hdr *)buf;
+       struct ast_iax_frame fr, *cur;
+       struct ast_frame f;
+       struct ast_channel *c;
+       struct iax_dpcache *dp;
+       int format;
+       int exists;
+       char rel0[256];
+       char rel1[255];
+       char empty[32]="";              /* Safety measure */
+       res = recvfrom(netsocket, buf, sizeof(buf), 0,(struct sockaddr *) &sin, &len);
+       if (res < 0) {
+               if (errno != ECONNREFUSED)
+                       ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
+               handle_error();
+               return 1;
+       }
+       if (res < sizeof(struct ast_iax_mini_hdr)) {
+               ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, sizeof(struct ast_iax_mini_hdr));
+               return 1;
+       }
+#ifdef DEBUG_SUPPORT
+       if (iaxdebug)
+               showframe(NULL, fh, 1, &sin);
+#endif
        if (ntohs(mh->callno) & AST_FLAG_FULL) {
                /* Get the destination call number */
                dcallno = ntohs(fh->dcallno);
@@ -1613,32 +3188,50 @@ 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))
+               if ((f.frametype == AST_FRAME_IAX) && ((f.subclass == AST_IAX_COMMAND_NEW) || (f.subclass == AST_IAX_COMMAND_REGREQ)))
                        new = NEW_ALLOW;
+       } else {
+               /* Don't knwo anything about it yet */
+               f.frametype = AST_FRAME_NULL;
+               f.subclass = 0;
        }
-       pthread_mutex_lock(&iaxs_lock);
+       ast_pthread_mutex_lock(&iaxs_lock);
        fr.callno = find_callno(ntohs(mh->callno) & ~AST_FLAG_FULL, dcallno, &sin, new);
        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 */
                if (ntohs(mh->callno) & AST_FLAG_FULL) {
                        /* We can only raw hangup control frames */
-                       if ((f.subclass != AST_IAX_COMMAND_INVAL) || (f.frametype != AST_FRAME_IAX))
-                               raw_hangup(&sin, ntohs(fh->dcallno), ntohs(mh->callno));
+                       if (((f.subclass != AST_IAX_COMMAND_INVAL) &&
+                                (f.subclass != AST_IAX_COMMAND_TXCNT) &&
+                                (f.subclass != AST_IAX_COMMAND_TXACC))||
+                           (f.frametype != AST_FRAME_IAX))
+                               raw_hangup(&sin, ntohs(fh->dcallno), ntohs(mh->callno) & ~AST_FLAG_FULL
+                               );
                }
-               pthread_mutex_unlock(&iaxs_lock);
+               ast_pthread_mutex_unlock(&iaxs_lock);
                return 1;
        }
-       iaxs[fr.callno]->peercallno = ntohs(mh->callno) & ~AST_FLAG_FULL;
+       if (((f.subclass != AST_IAX_COMMAND_TXCNT) &&
+            (f.subclass != AST_IAX_COMMAND_TXACC)) || (f.frametype != AST_FRAME_IAX))
+               iaxs[fr.callno]->peercallno = (short)(ntohs(mh->callno) & ~AST_FLAG_FULL);
        if (ntohs(mh->callno) & AST_FLAG_FULL) {
                if (option_debug)
                        ast_log(LOG_DEBUG, "Received packet %d, (%d, %d)\n", ntohs(fh->seqno), f.frametype, f.subclass);
                /* Check if it's out of order (and not an ACK or INVAL) */
                fr.seqno = ntohs(fh->seqno);
-               if (iaxs[fr.callno]->iseqno != fr.seqno) {
+               if ((iaxs[fr.callno]->iseqno != fr.seqno) &&
+                       (iaxs[fr.callno]->iseqno ||
+                               ((f.subclass != AST_IAX_COMMAND_TXCNT) &&
+                               (f.subclass != AST_IAX_COMMAND_TXACC)) ||
+                               (f.subclass != AST_FRAME_IAX))) {
                        if (
-                        ((f.subclass != AST_IAX_COMMAND_ACK) && (f.subclass != AST_IAX_COMMAND_INVAL)) ||
-                        (f.frametype != AST_FRAME_IAX)) {
+                        ((f.subclass != AST_IAX_COMMAND_ACK) &&
+                         (f.subclass != AST_IAX_COMMAND_INVAL) &&
+                         (f.subclass != AST_IAX_COMMAND_TXCNT) &&
+                         (f.subclass != AST_IAX_COMMAND_TXACC) &&
+                         (f.subclass != AST_IAX_COMMAND_VNAK)) ||
+                         (f.frametype != AST_FRAME_IAX)) {
                                /* If it's not an ACK packet, it's out of order. */
                                if (option_debug)
                                        ast_log(LOG_DEBUG, "Packet arrived out of order (expecting %d, got %d) (frametype = %d, subclass = %d)\n", 
@@ -1649,165 +3242,383 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                                        ((f.subclass != AST_IAX_COMMAND_ACK) && (f.subclass != AST_IAX_COMMAND_INVAL))) {
                                                if (option_debug)
                                                        ast_log(LOG_DEBUG, "Acking anyway\n");
-                                               send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.seqno);
+                                               send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.seqno);
                                        }
+                               } else {
+                                       /* Send a VNAK requesting retransmission */
+                                       iax_vnak(fr.callno);
                                }
-                               pthread_mutex_unlock(&iaxs_lock);
+                               ast_pthread_mutex_unlock(&iaxs_lock);
                                return 1;
                        }
                } else {
-                       /* Increment unless it's an ACK */
-                       if ((f.subclass != AST_IAX_COMMAND_ACK) ||
+                       /* Increment unless it's an ACK or VNAK */
+                       if (((f.subclass != AST_IAX_COMMAND_ACK) &&
+                           (f.subclass != AST_IAX_COMMAND_INVAL) &&
+                           (f.subclass != AST_IAX_COMMAND_TXCNT) &&
+                           (f.subclass != AST_IAX_COMMAND_TXACC) &&
+                               (f.subclass != AST_IAX_COMMAND_VNAK)) ||
                            (f.frametype != AST_FRAME_IAX))
                                iaxs[fr.callno]->iseqno++;
                }
                /* 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));
-                       pthread_mutex_unlock(&iaxs_lock);
+                       ast_pthread_mutex_unlock(&iaxs_lock);
                        return 1;
                }
                f.datalen = res - sizeof(struct ast_iax_full_hdr);
                if (f.datalen)
                        f.data = buf + sizeof(struct ast_iax_full_hdr);
                else
-                       f.data = NULL;
+                       f.data = empty;
                fr.ts = ntohl(fh->ts);
                /* Unless this is an ACK or INVAL frame, ack it */
                if ((f.frametype != AST_FRAME_IAX) || 
-                        ((f.subclass != AST_IAX_COMMAND_ACK) && (f.subclass != AST_IAX_COMMAND_INVAL))) 
-                       send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.seqno);
-               if (f.frametype == AST_FRAME_VOICE)
-                       iaxs[fr.callno]->voiceformat = f.subclass;
+                        ((f.subclass != AST_IAX_COMMAND_ACK) && 
+                         (f.subclass != AST_IAX_COMMAND_TXCNT) && 
+                         (f.subclass != AST_IAX_COMMAND_TXACC) && 
+                         (f.subclass != AST_IAX_COMMAND_INVAL) &&
+                         (f.subclass != AST_IAX_COMMAND_VNAK))) 
+                       send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.seqno);
+               if (f.frametype == AST_FRAME_VOICE) {
+                       if (f.subclass != iaxs[fr.callno]->voiceformat) {
+                                       iaxs[fr.callno]->voiceformat = f.subclass;
+                                       ast_log(LOG_DEBUG, "Ooh, voice format changed to %d\n", f.subclass);
+                                       if (iaxs[fr.callno]->owner) {
+                                               int orignative;
+                                               ast_pthread_mutex_lock(&iaxs[fr.callno]->owner->lock);
+                                               orignative = iaxs[fr.callno]->owner->nativeformats;
+                                               iaxs[fr.callno]->owner->nativeformats = f.subclass;
+                                               if (iaxs[fr.callno]->owner->readformat)
+                                                       ast_set_read_format(iaxs[fr.callno]->owner, iaxs[fr.callno]->owner->readformat);
+                                               iaxs[fr.callno]->owner->nativeformats = orignative;
+                                               ast_pthread_mutex_unlock(&iaxs[fr.callno]->owner->lock);
+                                       }
+                       }
+               }
                if (f.frametype == AST_FRAME_IAX) {
                        /* Handle the IAX pseudo frame itself */
                        if (option_debug)
                                ast_log(LOG_DEBUG, "IAX subclass %d received\n", f.subclass);
+                       /* Go through the motions of delivering the packet without actually doing so,
+                          unless this is a lag request since it will be done for real */
+                       if (f.subclass != AST_IAX_COMMAND_LAGRQ)
+                               schedule_delivery(&fr, 0);
                        switch(f.subclass) {
                        case AST_IAX_COMMAND_ACK:
                                /* Ack the packet with the given timestamp */
-                               pthread_mutex_lock(&iaxq.lock);
+                               ast_pthread_mutex_lock(&iaxq.lock);
                                for (cur = iaxq.head; cur ; cur = cur->next) {
                                        /* If it's our call, and our timestamp, mark -1 retries */
-                                       if ((fr.callno == cur->callno) && (fr.seqno == cur->seqno))
+                                       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);
+                                       }
+                               }
+                               ast_pthread_mutex_unlock(&iaxq.lock);
+                               break;
+                       case AST_IAX_COMMAND_QUELCH:
+                               if (iaxs[fr.callno]->state & IAX_STATE_STARTED)
+                                       iaxs[fr.callno]->quelch = 1;
+                               break;
+                       case AST_IAX_COMMAND_UNQUELCH:
+                               if (iaxs[fr.callno]->state & IAX_STATE_STARTED)
+                                       iaxs[fr.callno]->quelch = 0;
+                               break;
+                       case AST_IAX_COMMAND_TXACC:
+                               if (iaxs[fr.callno]->transferring == TRANSFER_BEGIN) {
+                                       /* Ack the packet with the given timestamp */
+                                       ast_pthread_mutex_lock(&iaxq.lock);
+                                       for (cur = iaxq.head; cur ; cur = cur->next) {
+                                               /* Cancel any outstanding txcnt's */
+                                               if ((fr.callno == cur->callno) && (cur->transfer))
+                                                       cur->retries = -1;
+                                       }
+                                       ast_pthread_mutex_unlock(&iaxq.lock);
+                                       snprintf(rel1, sizeof(rel1), "callno=%d;", iaxs[fr.callno]->callno);
+                                       send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_TXREADY, 0, rel1, strlen(rel1) + 1, -1);
+                                       iaxs[fr.callno]->transferring = TRANSFER_READY;
                                }
-                               pthread_mutex_unlock(&iaxq.lock);
                                break;
                        case AST_IAX_COMMAND_NEW:
+                               /* Ignore if it's already up */
+                               if (iaxs[fr.callno]->state & (IAX_STATE_STARTED | IAX_STATE_TBD))
+                                       break;
                                ((char *)f.data)[f.datalen] = '\0';
                                if (check_access(fr.callno, &sin, f.data, f.datalen)) {
                                        /* They're not allowed on */
-                                       send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No authority found", strlen("No authority found"), -1);
-                                       ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s'\n", inet_ntoa(sin.sin_addr), f.data);
-                                       /* XXX Not guaranteed to work, but probably does XXX */
-                                       pthread_mutex_lock(&iaxq.lock);
-                                       send_packet(iaxq.tail);
-                                       pthread_mutex_unlock(&iaxq.lock);
-                                       iax_destroy(fr.callno);
+                                       send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No authority found", strlen("No authority found"), -1);
+                                       ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s'\n", inet_ntoa(sin.sin_addr), (char *)f.data);
                                        break;
                                }
-                               if (!strlen(iaxs[fr.callno]->secret)) {
-                                       /* No authentication required, let them in */
-                                       send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACCEPT, 0, NULL, 0, -1);
-                                       iaxs[fr.callno]->state |= IAX_STATE_STARTED;
-                                       if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING)))
-                                               iax_destroy(fr.callno);
-                                       else
-                                               c->format = iaxs[fr.callno]->peerformats;
+                               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);
+                                               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);
+                                       } else {
+                                               /* Select an appropriate format */
+                                               format = iaxs[fr.callno]->peerformat & iax_capability;
+                                               if (!format) {
+                                                       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);
+                                                       } 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);
+                                                                       iaxs[fr.callno]->alreadygone = 1;
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                               /* 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));
+                                               }
+                                       }
                                        break;
                                }
                                authenticate_request(iaxs[fr.callno]);
                                iaxs[fr.callno]->state |= IAX_STATE_AUTHENTICATED;
                                break;
+                       case AST_IAX_COMMAND_DPREQ:
+                               /* Request status in the dialplan */
+                               ((char *)f.data)[f.datalen] = '\0';
+                               if ((iaxs[fr.callno]->state & IAX_STATE_TBD) && 
+                                       !(iaxs[fr.callno]->state & IAX_STATE_STARTED) && f.datalen) {
+                                       /* Must be started */
+                                       if (ast_exists_extension(NULL, iaxs[fr.callno]->context, (char *)f.data, 1, iaxs[fr.callno]->callerid)) {
+                                               strcpy(rel0, "exists");
+                                       } else if (ast_canmatch_extension(NULL, iaxs[fr.callno]->context, (char *)f.data, 1, iaxs[fr.callno]->callerid)) {
+                                               strcpy(rel0, "canexist");
+                                       } else {
+                                               strcpy(rel0, "nonexistant");
+                                       }
+                                       snprintf(rel1, sizeof(rel1), "number=%s;status=%s;ignorepat=%s;expirey=%d;",
+                                               (char *)f.data, rel0,
+                                               ast_ignore_pattern(iaxs[fr.callno]->context, (char *)f.data) ? "yes" : "no",
+                                               iaxdefaultdpcache);
+                                       send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_DPREP, 0, rel1, strlen(rel1) + 1, -1);
+                               }
+                               break;
                        case AST_IAX_COMMAND_HANGUP:
-#if 0
-                               iaxs[fr.callno]->error = ENOTCONN;
-#endif
+                               iaxs[fr.callno]->alreadygone = 1;
                                iax_destroy(fr.callno);
                                break;
                        case AST_IAX_COMMAND_REJECT:
                                if (f.data)
                                        ((char *)f.data)[f.datalen] = '\0';
-                               ast_log(LOG_WARNING, "Call rejected by %s: %s\n", inet_ntoa(iaxs[fr.callno]->addr.sin_addr), f.data);
+                               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);
                                break;
                        case AST_IAX_COMMAND_ACCEPT:
-                               if (f.data) {
+                               /* Ignore if call is already up or needs authentication or is a TBD */
+                               if (iaxs[fr.callno]->state & (IAX_STATE_STARTED | IAX_STATE_TBD | IAX_STATE_AUTHENTICATED))
+                                       break;
+                               if (f.data && f.datalen) {
                                        ((char *)f.data)[f.datalen]='\0';
                                        iax_getformats(fr.callno, (char *)f.data);
                                } else {
-                                       iaxs[fr.callno]->peerformats = iax_capability;
+                                       if (iaxs[fr.callno]->owner)
+                                               iaxs[fr.callno]->peerformat = iaxs[fr.callno]->owner->nativeformats;
+                                       else
+                                               iaxs[fr.callno]->peerformat = iax_capability;
                                }
                                if (option_verbose > 2)
-                                       ast_verbose(VERBOSE_PREFIX_3 "Call accepted by %s\n", inet_ntoa(iaxs[fr.callno]->addr.sin_addr));
-                               iaxs[fr.callno]->state |= IAX_STATE_STARTED;
-                               if (iaxs[fr.callno]->owner) {
-                                       /* Switch us to use a compatible format */
-                                       iaxs[fr.callno]->owner->format &= iaxs[fr.callno]->peerformats;
-
-                                       if (!iaxs[fr.callno]->owner->format) 
-                                               iaxs[fr.callno]->owner->format = iaxs[fr.callno]->peerformats & iax_capability;
-                                       if (!iaxs[fr.callno]->owner->format) {
-                                               ast_log(LOG_WARNING, "Unable to negotiate a common format with the peer.");
-                                               iaxs[fr.callno]->error = EBADE;
-                                               iax_destroy(fr.callno);
-                                       } else {
+                                       ast_verbose(VERBOSE_PREFIX_3 "Call accepted by %s (format %d)\n", inet_ntoa(iaxs[fr.callno]->addr.sin_addr), iaxs[fr.callno]->peerformat);
+                               if (!(iaxs[fr.callno]->peerformat & iaxs[fr.callno]->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 call to %s, format 0x%x incompatible with our capability 0x%x.\n", inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformat, iaxs[fr.callno]->capability);
+                               } else {
+                                       iaxs[fr.callno]->state |= IAX_STATE_STARTED;
+                                       if (iaxs[fr.callno]->owner) {
+                                               /* Switch us to use a compatible format */
+                                               iaxs[fr.callno]->owner->nativeformats = iaxs[fr.callno]->peerformat;
                                                if (option_verbose > 2)
-                                                       ast_verbose(VERBOSE_PREFIX_3 "Format for call is %d\n", iaxs[fr.callno]->owner->format);
+                                                       ast_verbose(VERBOSE_PREFIX_3 "Format for call is %d\n", iaxs[fr.callno]->owner->nativeformats);
+                                               /* Setup read/write formats properly. */
+                                               if (iaxs[fr.callno]->owner->writeformat)
+                                                       ast_set_write_format(iaxs[fr.callno]->owner, iaxs[fr.callno]->owner->writeformat);      
+                                               if (iaxs[fr.callno]->owner->readformat)
+                                                       ast_set_read_format(iaxs[fr.callno]->owner, iaxs[fr.callno]->owner->readformat);        
+                                       }
+                               }
+                               ast_pthread_mutex_lock(&dpcache_lock);
+                               dp = iaxs[fr.callno]->dpentries;
+                               while(dp) {
+                                       if (!(dp->flags & CACHE_FLAG_TRANSMITTED)) {
+                                               iax_dprequest(dp, fr.callno);
                                        }
-                                               
+                                       dp = dp->peer;
                                }
+                               ast_pthread_mutex_unlock(&dpcache_lock);
                                break;
                        case AST_IAX_COMMAND_PING:
+#ifdef BRIDGE_OPTIMIZATION
+                               if (iaxs[fr.callno]->bridgecallno > -1) {
+                                       /* If we're in a bridged call, just forward this */
+                                       forward_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_PING, fr.ts, NULL, 0, -1);
+                               } 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);
+                               }
+#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                         
                                break;
                        case AST_IAX_COMMAND_PONG:
+#ifdef BRIDGE_OPTIMIZATION
+                               if (iaxs[fr.callno]->bridgecallno > -1) {
+                                       /* Forward to the other side of the bridge */
+                                       forward_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_PONG, fr.ts, NULL, 0, -1);
+                               } else {
+                                       /* Calculate ping time */
+                                       iaxs[fr.callno]->pingtime =  calc_timestamp(iaxs[fr.callno], 0) - fr.ts;
+                               }
+#else
+                               /* Calculate ping time */
                                iaxs[fr.callno]->pingtime =  calc_timestamp(iaxs[fr.callno], 0) - fr.ts;
+#endif                                 
                                break;
                        case AST_IAX_COMMAND_LAGRQ:
                        case AST_IAX_COMMAND_LAGRP:
-                               /* A little strange -- We have to actually go through the motions of
-                                  delivering the packet.  In the very last step, it will be properly
-                                  handled by do_deliver */
-                               snprintf(src, sizeof(src), "LAGRQ-IAX/%s/%d", inet_ntoa(sin.sin_addr),fr.callno);
-                               f.src = src;
-                               f.mallocd = 0;
-                               f.offset = 0;
-                               fr.f = &f;
-                               f.timelen = 0;
-                               schedule_delivery(iaxfrdup2(&fr, 0));
+#ifdef BRIDGE_OPTIMIZATION
+                               if (iaxs[fr.callno]->bridgecallno > -1) {
+                                       forward_command(iaxs[fr.callno], AST_FRAME_IAX, f.subclass, fr.ts, NULL, 0, -1);
+                               } else {
+#endif                         
+                                       /* A little strange -- We have to actually go through the motions of
+                                          delivering the packet.  In the very last step, it will be properly
+                                          handled by do_deliver */
+                                       snprintf(src, sizeof(src), "LAGRQ-IAX/%s/%d", inet_ntoa(sin.sin_addr),fr.callno);
+                                       f.src = src;
+                                       f.mallocd = 0;
+                                       f.offset = 0;
+                                       fr.f = &f;
+                                       f.timelen = 0;
+                                       schedule_delivery(iaxfrdup2(&fr, 0), 1);
+#ifdef BRIDGE_OPTIMIZATION
+                               }
+#endif                         
                                break;
                        case AST_IAX_COMMAND_AUTHREQ:
+                               if (iaxs[fr.callno]->state & (IAX_STATE_STARTED | IAX_STATE_TBD)) {
+                                       ast_log(LOG_WARNING, "Call on %s is already up, can't start on it\n", iaxs[fr.callno]->owner ? iaxs[fr.callno]->owner->name : "<Unknown>");
+                                       break;
+                               }
                                ((char *)f.data)[f.datalen] = '\0';
-                               if (authenticate_reply(iaxs[fr.callno], &iaxs[fr.callno]->addr, (char *)f.data)) {
+                               if (authenticate_reply(iaxs[fr.callno], &iaxs[fr.callno]->addr, (char *)f.data, iaxs[fr.callno]->secret, iaxs[fr.callno]->outkey)) {
                                        ast_log(LOG_WARNING, 
                                                "I don't know how to authenticate %s to %s\n", 
-                                               f.data, inet_ntoa(iaxs[fr.callno]->addr.sin_addr));
-                                       iax_destroy(fr.callno);
+                                               (char *)f.data, inet_ntoa(iaxs[fr.callno]->addr.sin_addr));
                                }
                                break;
                        case AST_IAX_COMMAND_AUTHREP:
+                               /* Ignore once we've started */
+                               if (iaxs[fr.callno]->state & (IAX_STATE_STARTED | IAX_STATE_TBD)) {
+                                       ast_log(LOG_WARNING, "Call on %s is already up, can't start on it\n", iaxs[fr.callno]->owner ? iaxs[fr.callno]->owner->name : "<Unknown>");
+                                       break;
+                               }
                                ((char *)f.data)[f.datalen] = '\0';
                                if (authenticate_verify(iaxs[fr.callno], (char *)f.data)) {
                                        ast_log(LOG_NOTICE, "Host %s failed to authenticate as %s\n", inet_ntoa(iaxs[fr.callno]->addr.sin_addr), iaxs[fr.callno]->username);
-                                       send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No authority found", strlen("No authority found"), -1);
-                                       /* XXX Not guaranteed to work, but probably does XXX */
-                                       pthread_mutex_lock(&iaxq.lock);
-                                       send_packet(iaxq.tail);
-                                       pthread_mutex_unlock(&iaxq.lock);
-                                       iax_destroy(fr.callno);
+                                       send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No authority found", strlen("No authority found"), -1);
                                        break;
                                }
-                               /* Authentication is fine, go ahead */
-                               send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACCEPT, 0, NULL, 0, -1);
-                               iaxs[fr.callno]->state |= IAX_STATE_STARTED;
-                               if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING)))
-                                       iax_destroy(fr.callno);
-                               else
-                                       c->format = iaxs[fr.callno]->peerformats;
+                               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);
+                               } else {
+                                       /* Select an appropriate format */
+                                       format = iaxs[fr.callno]->peerformat & iax_capability;
+                                       if (!format) {
+                                               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);
+                                               } 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);
+                                                       }
+                                               }
+                                       }
+                                       /* 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));
+                                       }
+                               }
+                               break;
+                       case AST_IAX_COMMAND_DIAL:
+                               ((char *)f.data)[f.datalen] = '\0';
+                               if (iaxs[fr.callno]->state & IAX_STATE_TBD) {
+                                       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);
+                                       } 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;
+                                       }
+                               }
                                break;
                        case AST_IAX_COMMAND_INVAL:
                                iaxs[fr.callno]->error = ENOTCONN;
@@ -1815,11 +3626,115 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                if (option_debug)
                                        ast_log(LOG_DEBUG, "Destroying call %d\n", fr.callno);
                                break;
+                       case AST_IAX_COMMAND_VNAK:
+                               ast_log(LOG_DEBUG, "Sending VNAK\n");
+                               /* Force retransmission */
+                               vnak_retransmit(fr.callno, fr.seqno);
+                               break;
+                       case AST_IAX_COMMAND_REGREQ:
+                               if (f.data) 
+                                       ((char *)f.data)[f.datalen]='\0';
+                               if (register_verify(fr.callno, &sin, (char *)f.data)) {
+                                       send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REGREJ, 0, "Registration Refused", strlen("Registration Refused"), -1);
+                                       break;
+                               }
+                               if ((!strlen(iaxs[fr.callno]->secret) && !strlen(iaxs[fr.callno]->inkeys)) || (iaxs[fr.callno]->state & IAX_STATE_AUTHENTICATED)) {
+                                       if (update_registry(iaxs[fr.callno]->peer, &sin, fr.callno))
+                                               ast_log(LOG_WARNING, "Registry error\n");
+                                       break;
+                               }
+                               registry_authrequest(iaxs[fr.callno]->peer, fr.callno);
+                               break;
+                       case AST_IAX_COMMAND_REGACK:
+                               if (f.data)
+                                       ((char *)f.data)[f.datalen] = '\0';
+                               if (iax_ack_registry(f.data, &sin)) 
+                                       ast_log(LOG_WARNING, "Registration failure\n");
+                               iax_destroy(fr.callno);
+                               break;
+                       case AST_IAX_COMMAND_REGREJ:
+                               if (f.data)
+                                       ((char *)f.data)[f.datalen] = '\0';
+                               if (iaxs[fr.callno]->reg) {
+                                       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);
+                               break;
+                       case AST_IAX_COMMAND_REGAUTH:
+                               /* Authentication request */
+                               if (f.data)
+                                       ((char *)f.data)[f.datalen] = '\0';
+                               if (registry_rerequest(f.data, fr.callno, &sin)) 
+                                       send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No authority found", strlen("No authority found"), -1);
+                               break;
+                       case AST_IAX_COMMAND_TXREJ:
+                               iaxs[fr.callno]->transferring = 0;
+                               if (option_verbose > 2) 
+                                       ast_verbose(VERBOSE_PREFIX_3 "Channel '%s' unable to transfer\n", iaxs[fr.callno]->owner ? iaxs[fr.callno]->owner->name : "<Unknown>");
+                               memset(&iaxs[fr.callno]->transfer, 0, sizeof(iaxs[fr.callno]->transfer));
+                               if (iaxs[fr.callno]->bridgecallno > -1) {
+                                       if (iaxs[iaxs[fr.callno]->bridgecallno]->transferring) {
+                                               iaxs[iaxs[fr.callno]->bridgecallno]->transferring = 0;
+                                               send_command(iaxs[iaxs[fr.callno]->bridgecallno], AST_FRAME_IAX, AST_IAX_COMMAND_TXREJ, 0, NULL, 0, -1);
+                                       }
+                               }
+                               break;
+                       case AST_IAX_COMMAND_TXREADY:
+                               if (iaxs[fr.callno]->transferring == TRANSFER_BEGIN) {
+                                       iaxs[fr.callno]->transferring = TRANSFER_READY;
+                                       if (option_verbose > 2) 
+                                               ast_verbose(VERBOSE_PREFIX_3 "Channel '%s' ready to transfer\n", iaxs[fr.callno]->owner ? iaxs[fr.callno]->owner->name : "<Unknown>");
+                                       if (iaxs[fr.callno]->bridgecallno > -1) {
+                                               if (iaxs[iaxs[fr.callno]->bridgecallno]->transferring == TRANSFER_READY) {
+                                                       if (option_verbose > 2) 
+                                                               ast_verbose(VERBOSE_PREFIX_3 "Releasing %s and %s\n", iaxs[fr.callno]->owner ? iaxs[fr.callno]->owner->name : "<Unknown>",
+                                                                               iaxs[iaxs[fr.callno]->bridgecallno]->owner ? iaxs[iaxs[fr.callno]->bridgecallno]->owner->name : "<Unknown>");
+
+                                                       /* They're both ready, now release them. */
+                                                       iaxs[iaxs[fr.callno]->bridgecallno]->transferring = TRANSFER_RELEASED;
+                                                       iaxs[fr.callno]->transferring = TRANSFER_RELEASED;
+                                                       iaxs[iaxs[fr.callno]->bridgecallno]->alreadygone = 1;
+                                                       iaxs[fr.callno]->alreadygone = 1;
+
+                                                       /* Stop doing lag & ping requests */
+                                                       stop_stuff(fr.callno);
+                                                       stop_stuff(iaxs[fr.callno]->bridgecallno);
+
+                                                       /* Send the release message */
+                                                       snprintf(rel0, sizeof(rel0), "peercallno=%d;", iaxs[iaxs[fr.callno]->bridgecallno]->peercallno);
+                                                       snprintf(rel1, sizeof(rel1), "peercallno=%d;", iaxs[fr.callno]->peercallno);
+                                                       send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_TXREL, 0, rel0, strlen(rel0)+1, -1);
+                                                       send_command(iaxs[iaxs[fr.callno]->bridgecallno], AST_FRAME_IAX, AST_IAX_COMMAND_TXREL, 0, rel1, strlen(rel1)+1, -1);
+
+                                               }
+                                       }
+                               }
+                               break;
+                       case AST_IAX_COMMAND_TXREQ:
+                               if (f.data)
+                                       ((char *)f.data)[f.datalen] = '\0';
+                               try_transfer(iaxs[fr.callno], (char *)f.data);
+                               break;
+                       case AST_IAX_COMMAND_TXCNT:
+                               if (iaxs[fr.callno]->transferring)
+                                       send_command_transfer(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_TXACC, 0, NULL, 0);
+                               break;
+                       case AST_IAX_COMMAND_TXREL:
+                               if (f.data)
+                                       ((char *)f.data)[f.datalen] = '\0';
+                               complete_transfer(fr.callno, (char *)f.data);
+                               break;  
+                       case AST_IAX_COMMAND_DPREP:
+                               if (f.data)
+                                       ((char *)f.data)[f.datalen] = '\0';
+                               complete_dpreply(iaxs[fr.callno], (char *)f.data);
+                               break;
                        default:
                                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 */
-                       pthread_mutex_unlock(&iaxs_lock);
+                       ast_pthread_mutex_unlock(&iaxs_lock);
                        return 1;
                }
        } else {
@@ -1829,13 +3744,14 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                        f.subclass = iaxs[fr.callno]->voiceformat;
                else {
                        ast_log(LOG_WARNING, "Received mini frame before first full voice frame\n ");
-                       pthread_mutex_unlock(&iaxs_lock);
+                       iax_vnak(fr.callno);
+                       ast_pthread_mutex_unlock(&iaxs_lock);
                        return 1;
                }
                f.datalen = res - sizeof(struct ast_iax_mini_hdr);
                if (f.datalen < 0) {
                        ast_log(LOG_WARNING, "Datalen < 0?\n");
-                       pthread_mutex_unlock(&iaxs_lock);
+                       ast_pthread_mutex_unlock(&iaxs_lock);
                        return 1;
                }
                if (f.datalen)
@@ -1846,12 +3762,11 @@ 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)) {
-               pthread_mutex_unlock(&iaxs_lock);
+               ast_pthread_mutex_unlock(&iaxs_lock);
                return 1;
        }
        /* Common things */
-       snprintf(src, sizeof(src), "IAX/%s/%d", inet_ntoa(sin.sin_addr),fr.callno);
-       f.src = src;
+       snprintf(src, sizeof(src), "IAX/%s/%d", inet_ntoa(sin.sin_addr),fr.callno);     f.src = src;
        f.mallocd = 0;
        f.offset = 0;
        fr.f = &f;
@@ -1865,23 +3780,50 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                iaxs[fr.callno]->last = fr.ts;
                fr.outoforder = 0;
        } else {
-               ast_log(LOG_DEBUG, "Received out of order packet... (type=%d, subclass %d, ts = %d, last = %d)\n", f.frametype, f.subclass, fr.ts, iaxs[fr.callno]->last);
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Received out of order packet... (type=%d, subclass %d, ts = %d, last = %d)\n", f.frametype, f.subclass, fr.ts, iaxs[fr.callno]->last);
                fr.outoforder = -1;
        }
-       schedule_delivery(iaxfrdup2(&fr, 0));
+#ifdef BRIDGE_OPTIMIZATION
+       if (iaxs[fr.callno]->bridgecallno > -1) {
+               forward_delivery(&fr);
+       } else {
+               schedule_delivery(iaxfrdup2(&fr, 0), 1);
+       }
+#else
+       schedule_delivery(iaxfrdup2(&fr, 0), 1);
+#endif
        /* Always run again */
-       pthread_mutex_unlock(&iaxs_lock);
+       ast_pthread_mutex_unlock(&iaxs_lock);
        return 1;
 }
 
-static void free_ha(struct iax_ha *ha)
+static int iax_do_register(struct iax_registry *reg)
 {
-       struct iax_ha *hal;
-       while(ha) {
-               hal = ha;
-               ha = ha->next;
-               free(hal);
+       char requeststr[256] = "";
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Sending registration request for '%s'\n", reg->username);
+       if (reg->callno < 0) {
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Allocate call number\n");
+               reg->callno = find_callno(-1, -1, &reg->addr, NEW_FORCE);
+               if (reg->callno < 0) {
+                       ast_log(LOG_WARNING, "Unable to create call for registration\n");
+                       return -1;
+               } else if (option_debug)
+                       ast_log(LOG_DEBUG, "Registration created on call %d\n", reg->callno);
+               iaxs[reg->callno]->reg = reg;
        }
+       /* Schedule the next registration attempt */
+       if (reg->expire > -1)
+               ast_sched_del(sched, reg->expire);
+       /* Setup the registration a little early */
+       reg->expire  = ast_sched_add(sched, (5 * reg->refresh / 6) * 1000, iax_do_register_s, reg);
+       /* Send the request */
+       MYSNPRINTF "peer=%s;refresh=%d;", reg->username, reg->refresh);
+       send_command(iaxs[reg->callno],AST_FRAME_IAX, AST_IAX_COMMAND_REGREQ, 0, requeststr, strlen(requeststr) + 1, -1);
+       reg->regstate = REG_STATE_REGSENT;
+       return 0;
 }
 
 static void free_context(struct iax_context *con)
@@ -1897,36 +3839,51 @@ static void free_context(struct iax_context *con)
 static struct ast_channel *iax_request(char *type, int format, void *data)
 {
        int callno;
+       int res;
+       int sendani;
+       int fmt, native;
        struct sockaddr_in sin;
        char s[256];
        char *st;
        struct ast_channel *c;
-       strncpy(s, (char *)data, sizeof(s));
+       int capability = iax_capability;
+       strncpy(s, (char *)data, sizeof(s)-1);
        strtok(s, "/");
        strtok(s, "@");
        st = strtok(NULL, "@");
        if (!st)
                st = s;
        /* Populate our address from the given */
-       if (create_addr(&sin, st)) {
-               ast_log(LOG_WARNING, "Unable to assign address\n");
+       if (create_addr(&sin, &capability, &sendani, st, NULL)) {
                return NULL;
        }
-       pthread_mutex_lock(&iaxs_lock);
+       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;
        }
-       c = ast_iax_new(iaxs[callno], AST_STATE_DOWN);
+       /* Keep track of sendani flag */
+       iaxs[callno]->sendani = sendani;
+       c = ast_iax_new(iaxs[callno], AST_STATE_DOWN, capability);
        if (c) {
                /* Choose a format we can live with */
-               if (c->format & format)
-                       c->format &= format;
-               else 
-                       c->format = ast_translator_best_choice(format, c->format);
+               if (c->nativeformats & format)
+                       c->nativeformats &= format;
+               else {
+                       native = c->nativeformats;
+                       fmt = format;
+                       res = ast_translator_best_choice(&fmt, &native);
+                       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;
+               }
        }
-       pthread_mutex_unlock(&iaxs_lock);
+       ast_pthread_mutex_unlock(&iaxs_lock);
        return c;
 }
 
@@ -1938,11 +3895,11 @@ 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);
-       pthread_mutex_lock(&iaxs_lock);
+       ast_pthread_mutex_lock(&iaxs_lock);
        for(;;) {
                /* Go through the queue, sending messages which have not yet been
                   sent, and scheduling retransmissions if appropriate */
-               pthread_mutex_lock(&iaxq.lock);
+               ast_pthread_mutex_lock(&iaxq.lock);
                f = iaxq.head;
                while(f) {
                        freeme = NULL;
@@ -1965,27 +3922,29 @@ static void *network_thread(void *ignore)
                                        iaxq.count--;
                                        /* Free the frame */
                                        ast_frfree(f->f);
+                                       f->f = NULL;
                                        /* Free the iax frame */
                                        freeme = f;
                                } else {
                                        /* We need reliable delivery.  Schedule a retransmission */
                                        f->retries++;
-                                       ast_sched_add(sched, f->retrytime, attempt_transmit, f);
+                                       f->retrans = ast_sched_add(sched, f->retrytime, attempt_transmit, f);
                                }
                        }
                        f = f->next;
                        if (freeme)
-                               free(freeme);
+                               ast_iax_frame_free(freeme);
                }
-               pthread_mutex_unlock(&iaxq.lock);
-               pthread_mutex_unlock(&iaxs_lock);
+               ast_pthread_mutex_unlock(&iaxq.lock);
+               ast_pthread_mutex_unlock(&iaxs_lock);
                res = ast_sched_wait(sched);
                res = ast_io_wait(io, res);
-               pthread_mutex_lock(&iaxs_lock);
+               ast_pthread_mutex_lock(&iaxs_lock);
                if (res >= 0) {
                        ast_sched_runq(sched);
                }
        }
+       return NULL;
 }
 
 static int start_network_thread()
@@ -1997,83 +3956,132 @@ static struct iax_context *build_context(char *context)
 {
        struct iax_context *con = malloc(sizeof(struct iax_context));
        if (con) {
-               strncpy(con->context, context, sizeof(con->context));
+               strncpy(con->context, context, sizeof(con->context)-1);
                con->next = NULL;
        }
        return con;
 }
 
-static struct iax_ha *build_ha(char *sense, char *stuff)
-{
-       struct iax_ha *ha = malloc(sizeof(struct iax_ha));
-       char *nm;
-       if (ha) {
-               strtok(stuff, "/");
-               nm = strtok(NULL, "/");
-               if (!nm)
-                       nm = "255.255.255.255";
-               if (!inet_aton(stuff, &ha->netaddr)) {
-                       ast_log(LOG_WARNING, "%s not a valid IP\n", stuff);
-                       free(ha);
-                       return NULL;
-               }
-               if (!inet_aton(nm, &ha->netmask)) {
-                       ast_log(LOG_WARNING, "%s not a valid netmask\n", nm);
-                       free(ha);
-                       return NULL;
-               }
-               ha->netaddr.s_addr &= ha->netmask.s_addr;
-               if (!strcasecmp(sense, "a")) {
-                       ha->sense = IAX_SENSE_ALLOW;
-               } else {
-                       ha->sense = IAX_SENSE_DENY;
-               }
-               ha->next = NULL;
-       }
-       return ha;
-}
-
 static struct iax_peer *build_peer(char *name, struct ast_variable *v)
 {
        struct iax_peer *peer;
+       struct iax_peer *prev;
        int maskfound=0;
-       struct hostent *hp;
-       peer = malloc(sizeof(struct iax_peer));
+       int format;
+       int found=0;
+       prev = NULL;
+       ast_pthread_mutex_lock(&peerl.lock);
+       peer = peerl.peers;
+       while(peer) {
+               if (!strcasecmp(peer->name, name)) {    
+                       break;
+               }
+               prev = peer;
+               peer = peer->next;
+       }
        if (peer) {
+               found++;
+               /* Already in the list, remove it and it will be added back (or FREE'd) */
+               if (prev) {
+                       prev->next = peer->next;
+               } else {
+                       peerl.peers = peer->next;
+               }
+               ast_pthread_mutex_unlock(&peerl.lock);
+       } else {
+               ast_pthread_mutex_unlock(&peerl.lock);
+               peer = malloc(sizeof(struct iax_peer));
                memset(peer, 0, sizeof(struct iax_peer));
-               strncpy(peer->name, name, sizeof(peer->name));
-               peer->addr.sin_port = htons(AST_DEFAULT_IAX_PORTNO);
+               peer->expire = -1;
+       }
+       if (peer) {
+               if (!found) {
+                       strncpy(peer->name, name, sizeof(peer->name)-1);
+                       peer->addr.sin_port = htons(AST_DEFAULT_IAX_PORTNO);
+                       peer->expirey = expirey;
+               }
+               peer->capability = iax_capability;
                while(v) {
                        if (!strcasecmp(v->name, "secret")) 
-                               strncpy(peer->secret, v->value, sizeof(peer->secret));
+                               strncpy(peer->secret, v->value, sizeof(peer->secret)-1);
+                       else if (!strcasecmp(v->name, "auth")) 
+                               strncpy(peer->methods, v->value, sizeof(peer->methods)-1);
                        else if (!strcasecmp(v->name, "host")) {
                                if (!strcasecmp(v->value, "dynamic")) {
                                        /* They'll register with us */
                                        peer->dynamic = 1;
-                                       memset(&peer->addr.sin_addr, 0, 4);
+                                       if (!found) {
+                                               /* Initialize stuff iff we're not found, otherwise
+                                                  we keep going with what we had */
+                                               memset(&peer->addr.sin_addr, 0, 4);
+                                               if (peer->addr.sin_port) {
+                                                       /* If we've already got a port, make it the default rather than absolute */
+                                                       peer->defaddr.sin_port = peer->addr.sin_port;
+                                                       peer->addr.sin_port = 0;
+                                               }
+                                       }
                                } else {
+                                       /* Non-dynamic.  Make sure we become that way if we're not */
+                                       if (peer->expire > -1)
+                                               ast_sched_del(sched, peer->expire);
+                                       peer->expire = -1;
                                        peer->dynamic = 0;
-                                       hp = gethostbyname(v->value);
-                                       if (hp) {
-                                               memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
-                                       } else {
-                                               ast_log(LOG_WARNING, "Unable to lookup '%s'\n", v->value);
+                                       if (ast_get_ip(&peer->addr, v->value)) {
                                                free(peer);
                                                return NULL;
                                        }
-                                       if (!maskfound)
-                                               inet_aton("255.255.255.255", &peer->mask);
                                }
-                       }
-                       else if (!strcasecmp(v->name, "mask")) {
+                               if (!maskfound)
+                                       inet_aton("255.255.255.255", &peer->mask);
+                       } else if (!strcasecmp(v->name, "defaultip")) {
+                               if (ast_get_ip(&peer->defaddr, v->value)) {
+                                       free(peer);
+                                       return NULL;
+                               }
+                       } else if (!strcasecmp(v->name, "permit") ||
+                                          !strcasecmp(v->name, "deny")) {
+                               peer->ha = ast_append_ha(v->name, v->value, peer->ha);
+                       } else if (!strcasecmp(v->name, "mask")) {
                                maskfound++;
                                inet_aton(v->value, &peer->mask);
-                       } else if (!strcasecmp(v->name, "port"))
-                               peer->addr.sin_port = htons(atoi(v->value));
-                       else if (!strcasecmp(v->name, "username"))
-                               strncpy(peer->username, v->value, sizeof(peer->username));
+                       } else if (!strcasecmp(v->name, "context")) {
+                               if (!strlen(peer->context))
+                                       strncpy(peer->context, v->value, sizeof(peer->context) - 1);
+                       } else if (!strcasecmp(v->name, "port")) {
+                               if (peer->dynamic)
+                                       peer->defaddr.sin_port = htons(atoi(v->value));
+                               else
+                                       peer->addr.sin_port = htons(atoi(v->value));
+                       } else if (!strcasecmp(v->name, "username")) {
+                               strncpy(peer->username, v->value, sizeof(peer->username)-1);
+                       } else if (!strcasecmp(v->name, "allow")) {
+                               format = ast_getformatbyname(v->value);
+                               if (format < 1) 
+                                       ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value);
+                               else
+                                       peer->capability |= format;
+                       } else if (!strcasecmp(v->name, "disallow")) {
+                               format = ast_getformatbyname(v->value);
+                               if (format < 1) 
+                                       ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value);
+                               else
+                                       peer->capability &= ~format;
+                       } else if (!strcasecmp(v->name, "callerid")) {
+                               strncpy(peer->callerid, v->value, sizeof(peer->callerid)-1);
+                               peer->hascallerid=1;
+                       } else if (!strcasecmp(v->name, "sendani")) {
+                               peer->sendani = ast_true(v->value);
+                       } else if (!strcasecmp(v->name, "inkeys")) {
+                               strncpy(peer->inkeys, v->value, sizeof(peer->inkeys));
+                       } else if (!strcasecmp(v->name, "outkey")) {
+                               strncpy(peer->outkey, v->value, sizeof(peer->outkey));
+                       }
+
                        v=v->next;
                }
+               if (!strlen(peer->methods))
+                       strcpy(peer->methods, "md5,plaintext");
+               peer->delme = 0;
        }
        return peer;
 }
@@ -2082,11 +4090,11 @@ static struct iax_user *build_user(char *name, struct ast_variable *v)
 {
        struct iax_user *user;
        struct iax_context *con, *conl = NULL;
-       struct iax_ha *ha, *hal = NULL;
+       int format;
        user = (struct iax_user *)malloc(sizeof(struct iax_user));
        if (user) {
                memset(user, 0, sizeof(struct iax_user));
-               strncpy(user->name, name, sizeof(user->name));
+               strncpy(user->name, name, sizeof(user->name)-1);
                while(v) {
                        if (!strcasecmp(v->name, "context")) {
                                con = build_context(v->value);
@@ -2097,75 +4105,133 @@ static struct iax_user *build_user(char *name, struct ast_variable *v)
                                                user->contexts = con;
                                        conl = con;
                                }
-                       } else if (!strcasecmp(v->name, "allow") ||
+                       } else if (!strcasecmp(v->name, "permit") ||
                                           !strcasecmp(v->name, "deny")) {
-                               ha = build_ha(v->name, v->value);
-                               if (ha) {
-                                       if (hal)
-                                               hal->next = ha;
-                                       else
-                                               user->ha = ha;
-                                       hal = ha;
-                               }
+                               user->ha = ast_append_ha(v->name, v->value, user->ha);
                        } else if (!strcasecmp(v->name, "auth")) {
-                               strncpy(user->methods, v->value, sizeof(user->methods));
+                               strncpy(user->methods, v->value, sizeof(user->methods)-1);
                        } else if (!strcasecmp(v->name, "secret")) {
-                               strncpy(user->secret, v->value, sizeof(user->secret));
+                               strncpy(user->secret, v->value, sizeof(user->secret)-1);
+                       } else if (!strcasecmp(v->name, "callerid")) {
+                               strncpy(user->callerid, v->value, sizeof(user->callerid)-1);
+                               user->hascallerid=1;
+                       } else if (!strcasecmp(v->name, "accountcode")) {
+                               strncpy(user->accountcode, v->value, sizeof(user->accountcode)-1);
+                       } else if (!strcasecmp(v->name, "amaflags")) {
+                               format = ast_cdr_amaflags2int(v->value);
+                               if (format < 0) {
+                                       ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
+                               } else {
+                                       user->amaflags = format;
+                               }
+                       } else if (!strcasecmp(v->name, "inkeys")) {
+                               strncpy(user->inkeys, v->value, sizeof(user->inkeys));
                        }
                        v = v->next;
                }
        }
+       if (!strlen(user->methods)) {
+               if (strlen(user->secret)) {
+                       strncpy(user->methods, "md5,plaintext", sizeof(user->methods) - 1);
+                       if (strlen(user->inkeys))
+                               strncat(user->methods, ",rsa", sizeof(user->methods) - 1);
+               } else if (strlen(user->inkeys)) {
+                       strncpy(user->methods, "rsa", sizeof(user->methods) - 1);
+               } else
+                       strncpy(user->methods, "md5,plaintext", sizeof(user->methods) -1);
+       }
        return user;
 }
 
-int load_module()
-{
-       int res = 0;
+
+void delete_users(void){
+       struct iax_user *user, *userlast;
+       struct iax_peer *peer;
+       struct iax_registry *reg, *regl;
+
+       /* Delete all users */
+       ast_pthread_mutex_lock(&userl.lock);
+       for (user=userl.users;user;) {
+               ast_free_ha(user->ha);
+               free_context(user->contexts);
+               userlast = user;
+               user=user->next;
+               free(userlast);
+       }
+       userl.users=NULL;
+       ast_pthread_mutex_unlock(&userl.lock);
+
+       for (reg = registrations;reg;) {
+               regl = reg;
+               reg = reg->next;
+               if (regl->expire > -1)
+                       ast_sched_del(sched, regl->expire);
+               free(regl);
+       }
+       registrations = NULL;
+       ast_pthread_mutex_lock(&peerl.lock);
+       for (peer=peerl.peers;peer;) {
+               /* Assume all will be deleted, and we'll find out for sure later */
+               peer->delme = 1;
+               peer = peer->next;
+       }
+       ast_pthread_mutex_unlock(&peerl.lock);
+}
+
+void prune_peers(void){
+       /* Prune peers who still are supposed to be deleted */
+       struct iax_peer *peer, *peerlast, *peernext;
+       ast_pthread_mutex_lock(&peerl.lock);
+       peerlast = NULL;
+       for (peer=peerl.peers;peer;) {
+               peernext = peer->next;
+               if (peer->delme) {
+                       /* Delete it, it needs to disappear */
+                       if (peer->expire > -1)
+                               ast_sched_del(sched, peer->expire);
+                       free(peer);
+                       if (peerlast)
+                               peerlast->next = peernext;
+                       else
+                               peerl.peers = peernext;
+               } else
+                       peerlast = peer;
+               peer=peernext;
+       }
+       ast_pthread_mutex_unlock(&peerl.lock);
+}
+
+
+int set_config(char *config_file, struct sockaddr_in* sin){
        struct ast_config *cfg;
+       int capability=iax_capability;
        struct ast_variable *v;
-       struct iax_user *user;
-       struct iax_peer *peer;
        char *cat;
        char *utype;
        int format;
-       
-       struct sockaddr_in sin;
-       
-       sin.sin_family = AF_INET;
-       sin.sin_port = ntohs(AST_DEFAULT_IAX_PORTNO);
-       sin.sin_addr.s_addr = INADDR_ANY;
-       
-       io = io_context_create();
-       sched = sched_context_create();
-       
-       if (!io || !sched) {
-               ast_log(LOG_ERROR, "Out of memory\n");
-               return -1;
-       }
-
-       pthread_mutex_init(&iaxq.lock, NULL);
-       pthread_mutex_init(&userl.lock, NULL);
+       struct iax_user *user;
+       struct iax_peer *peer;
+       static unsigned short int last_port=0;
 
-       ast_cli_register(&cli_show_users);
-       ast_cli_register(&cli_show_channels);
-       ast_cli_register(&cli_show_peers);
-       ast_cli_register(&cli_set_jitter);
-#ifdef IAX_SIMULATOR
-       ast_cli_register(&delay_cli);
-       ast_cli_register(&deviation_cli);
-       ast_cli_register(&reliability_cli);
-       ast_cli_register(&sim_show_cli);
-#endif
-       cfg = ast_load(config);
+       cfg = ast_load(config_file);
        
        if (!cfg) {
-               ast_log(LOG_ERROR, "Unable to load config %s\n", config);
+               ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
                return -1;
        }
        v = ast_variable_browse(cfg, "general");
        while(v) {
-               if (!strcasecmp(v->name, "port")) 
-                       sin.sin_port = ntohs(atoi(v->value));
+               if (!strcasecmp(v->name, "port")){ 
+                       sin->sin_port = ntohs(atoi(v->value));
+                       if(last_port==0){
+                               last_port=sin->sin_port;
+#if    0
+                               ast_verbose("setting last port\n");
+#endif
+                       }
+                       else if(sin->sin_port != last_port)
+                               ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
+               }
                else if (!strcasecmp(v->name, "pingtime")) 
                        ping_time = atoi(v->value);
                else if (!strcasecmp(v->name, "maxjitterbuffer")) 
@@ -2177,16 +4243,16 @@ int load_module()
                else if (!strcasecmp(v->name, "dropcount")) 
                        iax_dropcount = atoi(v->value);
                else if (!strcasecmp(v->name, "bindaddr"))
-                       inet_aton(v->value, &sin.sin_addr);
+                       inet_aton(v->value, &sin->sin_addr);
                else if (!strcasecmp(v->name, "jitterbuffer"))
                        use_jitterbuffer = ast_true(v->value);
                else if (!strcasecmp(v->name, "bandwidth")) {
                        if (!strcasecmp(v->value, "low")) {
-                               iax_capability = IAX_CAPABILITY_LOWBANDWIDTH;
+                               capability = IAX_CAPABILITY_LOWBANDWIDTH;
                        } else if (!strcasecmp(v->value, "medium")) {
-                               iax_capability = IAX_CAPABILITY_MEDBANDWIDTH;
+                               capability = IAX_CAPABILITY_MEDBANDWIDTH;
                        } else if (!strcasecmp(v->value, "high")) {
-                               iax_capability = IAX_CAPABILITY_FULLBANDWIDTH;
+                               capability = IAX_CAPABILITY_FULLBANDWIDTH;
                        } else
                                ast_log(LOG_WARNING, "bandwidth must be either low, medium, or high\n");
                } else if (!strcasecmp(v->name, "allow")) {
@@ -2194,39 +4260,65 @@ int load_module()
                        if (format < 1) 
                                ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value);
                        else
-                               iax_capability |= format;
+                               capability |= format;
                } else if (!strcasecmp(v->name, "disallow")) {
                        format = ast_getformatbyname(v->value);
                        if (format < 1) 
                                ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value);
                        else
-                               iax_capability &= ~format;
+                               capability &= ~format;
+               } else if (!strcasecmp(v->name, "register")) {
+                       iax_register(v->value, v->lineno);
+               } else if (!strcasecmp(v->name, "tos")) {
+                       if (!strcasecmp(v->value, "lowdelay"))
+                               tos = IPTOS_LOWDELAY;
+                       else if (!strcasecmp(v->value, "throughput"))
+                               tos = IPTOS_THROUGHPUT;
+                       else if (!strcasecmp(v->value, "reliability"))
+                               tos = IPTOS_RELIABILITY;
+                       else if (!strcasecmp(v->value, "mincost"))
+                               tos = IPTOS_MINCOST;
+                       else if (!strcasecmp(v->value, "none"))
+                               tos = 0;
+                       else
+                               ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', 'mincost', or 'none'\n", v->lineno);
+               } else if (!strcasecmp(v->name, "accountcode")) {
+                       strncpy(accountcode, v->value, sizeof(accountcode)-1);
+               } else if (!strcasecmp(v->name, "amaflags")) {
+                       format = ast_cdr_amaflags2int(v->value);
+                       if (format < 0) {
+                               ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
+                       } else {
+                               amaflags = format;
+                       }
                }
                v = v->next;
        }
+       iax_capability = capability;
        cat = ast_category_browse(cfg, NULL);
        while(cat) {
                if (strcasecmp(cat, "general")) {
                        utype = ast_variable_retrieve(cfg, cat, "type");
                        if (utype) {
-                               if (!strcasecmp(utype, "user")) {
+                               if (!strcasecmp(utype, "user") || !strcasecmp(utype, "friend")) {
                                        user = build_user(cat, ast_variable_browse(cfg, cat));
                                        if (user) {
-                                               pthread_mutex_lock(&userl.lock);
+                                               ast_pthread_mutex_lock(&userl.lock);
                                                user->next = userl.users;
                                                userl.users = user;
-                                               pthread_mutex_unlock(&userl.lock);
+                                               ast_pthread_mutex_unlock(&userl.lock);
                                        }
-                               } else if (!strcasecmp(utype, "peer")) {
+                               }
+                               if (!strcasecmp(utype, "peer") || !strcasecmp(utype, "friend")) {
                                        peer = build_peer(cat, ast_variable_browse(cfg, cat));
                                        if (peer) {
-                                               pthread_mutex_lock(&peerl.lock);
+                                               ast_pthread_mutex_lock(&peerl.lock);
                                                peer->next = peerl.peers;
                                                peerl.peers = peer;
-                                               pthread_mutex_unlock(&peerl.lock);
+                                               ast_pthread_mutex_unlock(&peerl.lock);
                                        }
-                               } else {
-                                       ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, config);
+                               } else if (strcasecmp(utype, "user")) {
+                                       ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, config_file);
                                }
                        } else
                                ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat);
@@ -2234,24 +4326,415 @@ int load_module()
                cat = ast_category_browse(cfg, cat);
        }
        ast_destroy(cfg);
+       return capability;
+}
+
+static int reload_config(void)
+{
+       char *config = "iax.conf";
+       struct iax_registry *reg;
+       struct sockaddr_in dead_sin;
+       strncpy(accountcode, "", sizeof(accountcode)-1);
+       amaflags = 0;
+       srand(time(NULL));
+       delete_users();
+       set_config(config,&dead_sin);
+       prune_peers();
+       for (reg = registrations; reg; reg = reg->next)
+               iax_do_register(reg);
+       return 0;
+}
+
+int reload(void)
+{
+       return reload_config();
+}
+
+static int cache_get_callno(char *data)
+{
+       struct sockaddr_in sin;
+       int x;
+       char st[256], *s;
+       char *host;
+       char *username=NULL;
+       char *password=NULL;
+       char *context=NULL;
+       char requeststr[256] = "";
+       int callno;
+       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))
+                       return x;
+       }
+       /* No match found, we need to create a new one */
+       strncpy(st, data, sizeof(st)-1);
+       /* Grab the host */
+       s = strchr(st, '/');
+       if (s) {
+               *s = '\0';
+               s++;
+               context = s;
+       }
+       s = strchr(st, '@');
+       if (s) {
+               /* Get username/password if there is one */
+               *s='\0';
+               username=st;
+               password = strchr(username, ':');
+               if (password) {
+                       *password = '\0';
+                       password++;
+               }
+               s++;
+               host = s;
+       } else {
+               /* Just a hostname */
+               host = st;
+       }
+       /* Populate our address from the given */
+       if (create_addr(&sin, 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;
+       }
+       iaxs[callno]->capability = IAX_CAPABILITY_FULLBANDWIDTH;
+       MYSNPRINTF "exten=TBD;");
+       if (context)
+               MYSNPRINTF "context=%s;", context);
+       if (username)
+               MYSNPRINTF "username=%s;", username);
+       /* Remember, codec is irrelevent */
+       MYSNPRINTF "formats=%d;", IAX_CAPABILITY_FULLBANDWIDTH);
+       MYSNPRINTF "capability=%d;", IAX_CAPABILITY_FULLBANDWIDTH);
+       MYSNPRINTF "version=%d;", AST_IAX_PROTO_VERSION);
+       if (strlen(requeststr))
+               requeststr[strlen(requeststr) -1 ] = '\0';
+       /* Keep password handy */
+       if (password)
+               strncpy(iaxs[callno]->secret, password, sizeof(iaxs[callno]->secret)-1);
+       if (option_verbose > 2)
+               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);
+       return callno;
+}
+
+static struct iax_dpcache *find_cache(struct ast_channel *chan, char *data, char *context, char *exten, int priority)
+{
+       struct iax_dpcache *dp, *prev = NULL, *next;
+       struct timeval tv;
+       int x;
+       int com[2];
+       int timeout;
+       int old=0;
+       int outfd;
+       int abort;
+       int callno;
+       struct ast_channel *c;
+       struct ast_frame *f;
+       gettimeofday(&tv, NULL);
+       dp = dpcache;
+       while(dp) {
+               next = dp->next;
+               /* Expire old caches */
+               if ((tv.tv_sec > dp->expirey.tv_sec) ||
+                               ((tv.tv_sec == dp->expirey.tv_sec) && (tv.tv_usec > dp->expirey.tv_usec)))  {
+                               /* It's expired, let it disappear */
+                               if (prev)
+                                       prev->next = dp->next;
+                               else
+                                       dpcache = dp->next;
+                               if (!dp->peer && !(dp->flags & CACHE_FLAG_PENDING) && (dp->callno == -1)) {
+                                       /* Free memory and go again */
+                                       free(dp);
+                               } else {
+                                       ast_log(LOG_WARNING, "DP still has peer field or pending or callno (flags = %d, peer = %p callno = %d)\n", dp->flags, dp->peer, dp->callno);
+                               }
+                               dp = next;
+                               continue;
+               }
+               /* We found an entry that matches us! */
+               if (!strcmp(dp->peercontext, data) && !strcmp(dp->exten, exten)) 
+                       break;
+               dp = next;
+       }
+       if (!dp) {
+               /* No matching entry.  Create a new one. */
+               /* First, can we make a callno? */
+               callno = cache_get_callno(data);
+               if (callno < 0) {
+                       ast_log(LOG_WARNING, "Unable to generate call for '%s'\n", data);
+                       return NULL;
+               }
+               dp = malloc(sizeof(struct iax_dpcache));
+               if (!dp)
+                       return NULL;
+               memset(dp, 0, sizeof(struct iax_dpcache));
+               dp->callno = -1;
+               strncpy(dp->peercontext, data, sizeof(dp->peercontext)-1);
+               strncpy(dp->exten, exten, sizeof(dp->exten)-1);
+               gettimeofday(&dp->expirey, NULL);
+               dp->orig = dp->expirey;
+               /* Expires in 30 mins by default */
+               dp->expirey.tv_sec += iaxdefaultdpcache;
+               dp->next = dpcache;
+               dp->flags = CACHE_FLAG_PENDING;
+               for (x=0;x<sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++)
+                       dp->waiters[x] = -1;
+               dpcache = dp;
+               dp->peer = iaxs[callno]->dpentries;
+               iaxs[callno]->dpentries = dp;
+               /* Send the request if we're already up */
+               if (iaxs[callno]->state & IAX_STATE_STARTED)
+                       iax_dprequest(dp, callno);
+       }
+       /* By here we must have a dp */
+       if (dp->flags & CACHE_FLAG_PENDING) {
+               /* Okay, here it starts to get nasty.  We need a pipe now to wait
+                  for a reply to come back so long as it's pending */
+               for (x=0;x<sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++) {
+                       /* Find an empty slot */
+                       if (dp->waiters[x] < 0)
+                               break;
+               }
+               if (x >= sizeof(dp->waiters) / sizeof(dp->waiters[0])) {
+                       ast_log(LOG_WARNING, "No more waiter positions available\n");
+                       return NULL;
+               }
+               if (pipe(com)) {
+                       ast_log(LOG_WARNING, "Unable to create pipe for comm\n");
+                       return NULL;
+               }
+               dp->waiters[x] = com[1];
+               /* Okay, now we wait */
+               timeout = iaxdefaulttimeout * 1000;
+               /* Temporarily unlock */
+               ast_pthread_mutex_unlock(&dpcache_lock);
+               /* Defer any dtmf */
+               if (chan)
+                       old = ast_channel_defer_dtmf(chan);
+               abort = 0;
+               while(timeout) {
+                       c = ast_waitfor_nandfds(&chan, chan ? 1 : 0, &com[0], 1, NULL, &outfd, &timeout);
+                       if (outfd > -1) {
+                               break;
+                       }
+                       if (c) {
+                               f = ast_read(c);
+                               if (f)
+                                       ast_frfree(f);
+                               else {
+                                       /* Got hung up on, abort! */
+                                       break;
+                                       abort = 1;
+                               }
+                       }
+               }
+               if (!timeout) {
+                       ast_log(LOG_WARNING, "Timeout waiting for %s exten %s\n", data, exten);
+               }
+               ast_pthread_mutex_lock(&dpcache_lock);
+               dp->waiters[x] = -1;
+               close(com[1]);
+               close(com[0]);
+               if (abort) {
+                       /* Don't interpret anything, just abort.  Not sure what th epoint
+                         of undeferring dtmf on a hung up channel is but hey whatever */
+                       if (!old && chan)
+                               ast_channel_undefer_dtmf(chan);
+                       return NULL;
+               }
+               if (!(dp->flags & CACHE_FLAG_TIMEOUT)) {
+                       /* Now to do non-independent analysis the results of our wait */
+                       if (dp->flags & CACHE_FLAG_PENDING) {
+                               /* Still pending... It's a timeout.  Wake everybody up.  Consider it no longer
+                                  pending.  Don't let it take as long to timeout. */
+                               dp->flags &= ~CACHE_FLAG_PENDING;
+                               dp->flags |= CACHE_FLAG_TIMEOUT;
+                               /* Expire after only 60 seconds now.  This is designed to help reduce backlog in heavily loaded
+                                  systems without leaving it unavailable once the server comes back online */
+                               dp->expirey.tv_sec = dp->orig.tv_sec + 60;
+                               for (x=0;x<sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++)
+                                       if (dp->waiters[x] > -1)
+                                               write(dp->waiters[x], "asdf", 4);
+                       }
+               }
+               /* Our caller will obtain the rest */
+               if (!old && chan)
+                       ast_channel_undefer_dtmf(chan);
+       }
+       return dp;      
+}
+
+static int iax_exists(struct ast_channel *chan, char *context, char *exten, int priority, char *callerid, char *data)
+{
+       struct iax_dpcache *dp;
+       int res = 0;
+#if 0
+       ast_log(LOG_NOTICE, "iax_exists: 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_EXISTS)
+                       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_canmatch(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_canmatch: 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_CANEXIST)
+                       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];
+       char req[256];
+       char *ncontext;
+       struct iax_dpcache *dp;
+       struct ast_app *dial;
+#if 0
+       ast_log(LOG_NOTICE, "iax_exec: con: %s, exten: %s, pri: %d, cid: %s, data: %s, newstack: %d\n", context, exten, priority, callerid ? callerid : "<unknown>", data, newstack);
+#endif
+       if (priority != 1)
+               return -1;
+       ast_pthread_mutex_lock(&dpcache_lock);
+       dp = find_cache(chan, data, context, exten, priority);
+       if (dp) {
+               if (dp->flags & CACHE_FLAG_EXISTS) {
+                       strncpy(odata, data, sizeof(odata)-1);
+                       ncontext = strchr(odata, '/');
+                       if (ncontext) {
+                               *ncontext = '\0';
+                               ncontext++;
+                               snprintf(req, sizeof(req), "IAX/%s/%s@%s", odata, exten, ncontext);
+                       } else {
+                               snprintf(req, sizeof(req), "IAX/%s/%s", odata, exten);
+                       }
+                       if (option_verbose > 2)
+                               ast_verbose(VERBOSE_PREFIX_3 "Executing Dial('%s')\n", req);
+               } else {
+                       ast_pthread_mutex_unlock(&dpcache_lock);
+                       ast_log(LOG_WARNING, "Can't execute non-existant extension '%s[@%s]' in data '%s'\n", exten, context, data);
+                       return -1;
+               }
+       }
+       ast_pthread_mutex_unlock(&dpcache_lock);
+       dial = pbx_findapp("Dial");
+       if (dial) {
+               pbx_exec(chan, dial, req, newstack);
+       } else {
+               ast_log(LOG_WARNING, "No dial application registered\n");
+       }
+       return -1;
+}
+
+static struct ast_switch iax_switch = 
+{
+       name:                   "IAX",
+       description:    "IAX Remote Dialplan Switch",
+       exists:                 iax_exists,
+       canmatch:               iax_canmatch,
+       exec:                   iax_exec,
+};
+
+int load_module(void)
+{
+       char *config = "iax.conf";
+       int res = 0;
+       struct iax_registry *reg;
+       
+       struct sockaddr_in sin;
+       
+       /* Seed random number generator */
+       srand(time(NULL));
+       
+       sin.sin_family = AF_INET;
+       sin.sin_port = ntohs(AST_DEFAULT_IAX_PORTNO);
+       sin.sin_addr.s_addr = INADDR_ANY;
+       
+       io = io_context_create();
+       sched = sched_context_create();
+       
+       if (!io || !sched) {
+               ast_log(LOG_ERROR, "Out of memory\n");
+               return -1;
+       }
+
+       ast_pthread_mutex_init(&iaxq.lock);
+       ast_pthread_mutex_init(&userl.lock);
+
+       ast_cli_register(&cli_show_users);
+       ast_cli_register(&cli_show_channels);
+       ast_cli_register(&cli_show_peers);
+       ast_cli_register(&cli_show_registry);
+       ast_cli_register(&cli_debug);
+       ast_cli_register(&cli_no_debug);
+       ast_cli_register(&cli_set_jitter);
+       ast_cli_register(&cli_show_stats);
+       ast_cli_register(&cli_show_cache);
+
+       set_config(config,&sin);
+
        if (ast_channel_register(type, tdesc, iax_capability, iax_request)) {
                ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
                unload_module();
                return -1;
        }
+
+       if (ast_register_switch(&iax_switch)) 
+               ast_log(LOG_ERROR, "Unable to register IAX switch\n");
        
        /* Make a UDP socket */
        netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
-               
+       
        if (netsocket < 0) {
                ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
                return -1;
        }
-       if (bind(netsocket, &sin, sizeof(sin))) {
-               ast_log(LOG_ERROR, "Unable to bind to %s port %d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+       if (bind(netsocket,(struct sockaddr *)&sin, sizeof(sin))) {
+               ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), strerror(errno));
                return -1;
        }
 
+       if (option_verbose > 1)
+               ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos);
+
+       if (setsockopt(netsocket, SOL_IP, IP_TOS, &tos, sizeof(tos))) 
+               ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
+       
        if (!res) {
                res = start_network_thread();
                if (option_verbose > 1) 
@@ -2260,6 +4743,8 @@ int load_module()
                ast_log(LOG_ERROR, "Unable to start network thread\n");
                close(netsocket);
        }
+       for (reg = registrations; reg; reg = reg->next)
+               iax_do_register(reg);
        return res;
 }
 
@@ -2270,8 +4755,6 @@ char *description()
 
 int unload_module()
 {
-       struct iax_user *user, *userlast;
-       struct iax_peer *peer, *peerlast;
        int x;
        /* Cancel the network thread, close the net socket */
        pthread_cancel(netthreadid);
@@ -2284,33 +4767,23 @@ int unload_module()
        ast_cli_unregister(&cli_show_channels);
        ast_cli_unregister(&cli_show_peers);
        ast_cli_unregister(&cli_set_jitter);
-#ifdef IAX_SIMULATOR
-       ast_cli_unregister(&delay_cli);
-       ast_cli_unregister(&deviation_cli);
-       ast_cli_unregister(&reliability_cli);
-       ast_cli_unregister(&sim_show_cli);
-#endif
-       for (user=userl.users;user;) {
-               free_ha(user->ha);
-               free_context(user->contexts);
-               userlast = user;
-               user=user->next;
-               free(userlast);
-       }
-       for (peer=peerl.peers;peer;) {
-               peerlast = peer;
-               peer=peer->next;
-               free(peerlast);
-       }
+       ast_cli_unregister(&cli_show_stats);
+       ast_cli_unregister(&cli_show_cache);
+       ast_unregister_switch(&iax_switch);
+       delete_users();
        return 0;
 }
 
 int usecount()
 {
        int res;
-       pthread_mutex_lock(&usecnt_lock);
+       ast_pthread_mutex_lock(&usecnt_lock);
        res = usecnt;
-       pthread_mutex_unlock(&usecnt_lock);
+       ast_pthread_mutex_unlock(&usecnt_lock);
        return res;
 }
 
+char *key()
+{
+       return ASTERISK_GPL_KEY;
+}