diff --git a/CHANGES b/CHANGES
index 00897505962a27a2c53d4eb3a048fd7d4d51303f..f8e0cadeb7c8103b3771799951051b85fcd095e4 100755
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,4 @@
+ -- Add experimental "IAX2" protocol
  -- Add "Enhanced" AGI with audio pass-through (voice recognition anyone?)
  -- Choose best priority from codec from allow/disallow
  -- Reject SIP calls to self
diff --git a/Makefile b/Makefile
index 7c8456205e05e7268a03bc5f8b85383c9771b2c8..cd2a4a1457cdf269c32255bce0a57334f62d92b0 100755
--- a/Makefile
+++ b/Makefile
@@ -40,7 +40,7 @@ PROC=$(shell uname -m)
 
 DEBUG=-g #-pg
 INCLUDE=-Iinclude -I../include
-CFLAGS=-pipe  -Wall -Wmissing-prototypes -Wmissing-declarations $(DEBUG) $(INCLUDE) -D_REENTRANT -D_GNU_SOURCE #-DMAKE_VALGRIND_HAPPY
+CFLAGS=-pipe  -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(DEBUG) $(INCLUDE) -D_REENTRANT -D_GNU_SOURCE #-DMAKE_VALGRIND_HAPPY
 #CFLAGS+=-O6
 CFLAGS+=$(shell if $(CC) -march=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=$(PROC)"; fi)
 CFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-fsigned-char"; fi)
diff --git a/channels/Makefile b/channels/Makefile
index dc7eaf4860ab2c1903f92b45d59de4602674b57d..8105e7821133b05bdd212592edb369d9781c05f5 100755
--- a/channels/Makefile
+++ b/channels/Makefile
@@ -14,7 +14,7 @@
 CHANNEL_LIBS=chan_modem.so chan_iax.so chan_sip.so \
 	     chan_modem_aopen.so chan_oss.so \
              chan_modem_bestdata.so chan_modem_i4l.so \
-             chan_agent.so chan_mgcp.so
+             chan_agent.so chan_mgcp.so chan_iax2.so
 
 #
 # If you really want VoFR you can have it :-P
diff --git a/channels/chan_iax.c b/channels/chan_iax.c
index d48b24f552eb55550d7d0b329d9c1d5aef3c9bb3..0c3b905b5624a28cb1c124e073db874422fdf8f8 100755
--- a/channels/chan_iax.c
+++ b/channels/chan_iax.c
@@ -871,7 +871,7 @@ static int do_deliver(void *data)
 	return res;
 }
 
-static int handle_error()
+static int handle_error(void)
 {
 	/* XXX Ideally we should figure out why an error occured and then abort those
 	   rather than continuing to try.  Unfortunately, the published interface does
@@ -4279,7 +4279,7 @@ static void *network_thread(void *ignore)
 	return NULL;
 }
 
-static int start_network_thread()
+static int start_network_thread(void)
 {
 	return pthread_create(&netthreadid, NULL, network_thread, NULL);
 }
@@ -4556,7 +4556,7 @@ void prune_peers(void){
 }
 
 
-int set_config(char *config_file, struct sockaddr_in* sin){
+static int set_config(char *config_file, struct sockaddr_in* sin){
 	struct ast_config *cfg;
 	int capability=iax_capability;
 	struct ast_variable *v;
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
new file mode 100755
index 0000000000000000000000000000000000000000..76efd99e5cdcc65643b532c68e89525dee1da1eb
--- /dev/null
+++ b/channels/chan_iax2.c
@@ -0,0 +1,5351 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Implementation of Inter-Asterisk eXchange Version 2
+ *
+ * Copyright (C) 2003, Digium
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+#include <asterisk/lock.h>
+#include <asterisk/frame.h> 
+#include <asterisk/channel.h>
+#include <asterisk/channel_pvt.h>
+#include <asterisk/logger.h>
+#include <asterisk/module.h>
+#include <asterisk/pbx.h>
+#include <asterisk/sched.h>
+#include <asterisk/io.h>
+#include <asterisk/config.h>
+#include <asterisk/options.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 <asterisk/manager.h>
+#include <asterisk/callerid.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 <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <fcntl.h>
+
+#include "iax2.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
+
+#define DEBUG_SUPPORT
+
+/* Sample over last 100 units to determine historic jitter */
+#define GAMMA (0.01)
+
+static char *desc = "Inter Asterisk eXchange (Ver 2)";
+static char *tdesc = "Inter Asterisk eXchange Drver (Ver 2)";
+static char *type = "IAX2";
+
+static char context[80] = "default";
+
+static int max_retries = 4;
+static int ping_time = 20;
+static int lagrq_time = 10;
+static int nextcallno = 0;
+static int maxjitterbuffer=3000;
+
+static int iaxdefaultdpcache=10 * 60;	/* Cache dialplan entries for 10 minutes by default */
+
+static int iaxdefaulttimeout = 5;		/* Default to wait no more than 5 seconds for a reply to come back */
+
+static int netsocket = -1;
+
+static int tos = 0;
+
+static int expirey = AST_DEFAULT_REG_EXPIRE;
+
+static int usecnt;
+static pthread_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER;
+
+int (*regfunk)(char *username, int onoff) = NULL;
+
+/* Ethernet, etc */
+#define IAX_CAPABILITY_FULLBANDWIDTH 	0xFFFF
+/* T1, maybe ISDN */
+#define IAX_CAPABILITY_MEDBANDWIDTH 	(IAX_CAPABILITY_FULLBANDWIDTH & \
+									~AST_FORMAT_SLINEAR & \
+									~AST_FORMAT_ULAW & \
+									~AST_FORMAT_ALAW) 
+/* A modem */
+#define IAX_CAPABILITY_LOWBANDWIDTH		(IAX_CAPABILITY_MEDBANDWIDTH & \
+									~AST_FORMAT_MP3 & \
+									~AST_FORMAT_ADPCM)
+
+#define IAX_CAPABILITY_LOWFREE		(IAX_CAPABILITY_LOWBANDWIDTH & \
+									 ~AST_FORMAT_G723_1)
+
+
+#define DEFAULT_MAXMS		2000		/* Must be faster than 2 seconds by default */
+#define DEFAULT_FREQ_OK		60 * 1000		/* How often to check for the host to be up */
+#define DEFAULT_FREQ_NOTOK	10 * 1000		/* How often to check, if the host is down... */
+
+static	struct io_context *io;
+static	struct sched_context *sched;
+
+static int iax2_capability = IAX_CAPABILITY_FULLBANDWIDTH;
+
+static int iax2_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_STATE_TBD			(1 << 2)
+
+struct iax2_context {
+	char context[AST_MAX_EXTENSION];
+	struct iax2_context *next;
+};
+
+struct iax2_user {
+	char name[80];
+	char secret[80];
+	int authmethods;
+	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 iax2_context *contexts;
+	struct iax2_user *next;
+};
+
+struct iax2_peer {
+	char name[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;					/* 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 */
+	int authmethods;				/* Authentication methods (IAX_AUTH_*) */
+	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 */
+
+	/* Qualification */
+	int callno;					/* Call number of POKE request */
+	int pokeexpire;					/* When to expire poke */
+	int lastms;					/* How long last response took (in ms), or -1 for no response */
+	int maxms;					/* Max ms we will accept for the host to be up, 0 to not monitor */
+	
+	struct ast_ha *ha;
+	struct iax2_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 iax2_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 iax2_registry *next;
+};
+
+struct iax2_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
+#define MAX_JITTER_BUFFER 50
+
+/* If we have more than this much excess real jitter buffer, srhink it. */
+static int max_jitter_buffer = MAX_JITTER_BUFFER;
+
+struct chan_iax2_pvt {
+	/* Pipes for communication.  pipe[1] belongs to the
+	   network thread (write), and pipe[0] belongs to the individual 
+	   channel (read) */
+	/* 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 */
+	unsigned int lastsent;
+	/* Ping time */
+	unsigned int pingtime;
+	/* Max time for initial response */
+	int maxtime;
+	/* Peer Address */
+	struct sockaddr_in addr;
+	/* Our call number */
+	unsigned short callno;
+	/* Peer callno */
+	unsigned short peercallno;
+	/* 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 */
+	struct timeval rxcore;
+	/* Historical delivery time */
+	int history[MEMORY_SIZE];
+	/* Current base jitterbuffer */
+	int jitterbuffer;
+	/* Current jitter measure */
+	int jitter;
+	/* Historic jitter value */
+	int historicjitter;
+	/* LAG */
+	int lag;
+	/* Error, as discovered by the manager */
+	int error;
+	/* 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 sequence number they have not yet acknowledged */
+	unsigned short rseqno;
+	/* Next incoming sequence number */
+	unsigned short iseqno;
+	/* Last incoming sequence number we have acknowledged */
+	unsigned short aseqno;
+	/* Peer name */
+	char peer[80];
+	/* Default Context */
+	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 */
+	char exten[AST_MAX_EXTENSION];
+	/* Expected Username */
+	char username[80];
+	/* Expected Secret */
+	char secret[80];
+	/* permitted authentication methods */
+	int authmethods;
+	/* 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 iax2_registry *reg;
+	/* Associated peer for poking */
+	struct iax2_peer *peerpoke;
+
+	/* 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 */
+	unsigned short transfercallno;
+
+	/* Status of knowledge of peer ADSI capability */
+	int peeradsicpe;
+	
+	/* Who we are bridged to */
+	unsigned short bridgecallno;
+	int pingid;			/* Transmit PING request */
+	int lagid;			/* Retransmit lag request */
+	int autoid;			/* Auto hangup for Dialplan requestor */
+	int initid;			/* Initial peer auto-congest ID (based on qualified peers) */
+	char dproot[AST_MAX_EXTENSION];
+	char accountcode[20];
+	int amaflags;
+	struct iax2_dpcache *dpentries;
+};
+
+#define DIRECTION_INGRESS 1
+#define DIRECTION_OUTGRESS 2
+
+struct ast_iax2_frame {
+	/* Actual, isolated frame */
+	struct ast_frame *f;
+	/* /Our/ call number */
+	unsigned short callno;
+	/* Start of raw frame (outgoing only) */
+	void *data;
+	/* Length of frame (outgoing only) */
+	int datalen;
+	/* How many retries so far? */
+	int retries;
+	/* Outgoing relative timestamp (ms) */
+	unsigned int ts;
+	/* How long to wait before retrying */
+	int retrytime;
+	/* Are we received out of order?  */
+	int outoforder;
+	/* Have we been sent at all yet? */
+	int sentyet;
+	/* Outgoing Packet sequence number */
+	int oseqno;
+	/* Next expected incoming packet sequence number */
+	int iseqno;
+	/* 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_iax2_frame *next;
+	struct ast_iax2_frame *prev;
+};
+
+struct iax_ies {
+	char *called_number;
+	char *calling_number;
+	char *calling_ani;
+	char *calling_name;
+	char *called_context;
+	char *username;
+	char *password;
+	int capability;
+	int format;
+	char *language;
+	int version;
+	int adsicpe;
+	char *dnid;
+	int authmethods;
+	char *challenge;
+	char *md5_result;
+	char *rsa_result;
+	struct sockaddr_in *apparent_addr;
+	unsigned short refresh;
+	unsigned short dpstatus;
+	unsigned short callno;
+	char *cause;
+};
+
+struct iax_ie_data {
+	unsigned char buf[1024];
+	int pos;
+};
+
+static struct ast_iax2_queue {
+	struct ast_iax2_frame *head;
+	struct ast_iax2_frame *tail;
+	int count;
+	pthread_mutex_t lock;
+} iaxq;
+
+static struct ast_user_list {
+	struct iax2_user *users;
+	pthread_mutex_t lock;
+} userl;
+
+static struct ast_peer_list {
+	struct iax2_peer *peers;
+	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)
+/* Matchmore */
+#define CACHE_FLAG_MATCHMORE	(1 << 7)
+
+static struct iax2_dpcache {
+	char peercontext[AST_MAX_EXTENSION];
+	char exten[AST_MAX_EXTENSION];
+	struct timeval orig;
+	struct timeval expirey;
+	int flags;
+	unsigned short callno;
+	int waiters[256];
+	struct iax2_dpcache *next;
+	struct iax2_dpcache *peer;	/* For linking in peers */
+} *dpcache;
+
+pthread_mutex_t dpcache_lock;
+
+static struct iax2_ie {
+	int ie;
+	char *name;
+} ies[] = {
+	{ IAX_IE_CALLED_NUMBER, "CALLED NUMBER" },
+	{ IAX_IE_CALLING_NUMBER, "CALLING NUMBER" },
+	{ IAX_IE_CALLING_NUMBER, "ANI" },
+	{ IAX_IE_CALLING_NAME, "CALLING NAME" },
+	{ IAX_IE_CALLED_CONTEXT, "CALLED CONTEXT" },
+	{ IAX_IE_USERNAME, "USERNAME" },
+	{ IAX_IE_PASSWORD, "PASSWORD" },
+	{ IAX_IE_CAPABILITY, "CAPABILITY" },
+	{ IAX_IE_FORMAT, "FORMAT" },
+	{ IAX_IE_LANGUAGE, "LANGUAGE" },
+	{ IAX_IE_VERSION, "VERSION" },
+	{ IAX_IE_ADSICPE, "ADSICPE" },
+	{ IAX_IE_DNID, "DNID" },
+	{ IAX_IE_AUTHMETHODS, "AUTHMETHODS" },
+	{ IAX_IE_CHALLENGE, "CHALLENGE" },
+	{ IAX_IE_MD5_RESULT, "MD5 RESULT" },
+	{ IAX_IE_RSA_RESULT, "RSA RESULT" },
+	{ IAX_IE_APPARENT_ADDR, "APPARENT ADDRESS" },
+	{ IAX_IE_REFRESH, "REFRESH" },
+	{ IAX_IE_DPSTATUS, "DIALPLAN STATUS" },
+	{ IAX_IE_CALLNO, "CALL NUMBER" },
+	{ IAX_IE_CAUSE, "CAUSE" },
+};
+
+static char *ie2str(int ie)
+{
+	int x;
+	for (x=0;x<sizeof(ies) / sizeof(ies[0]); x++) {
+		if (ies[x].ie == ie)
+			return ies[x].name;
+	}
+	return "Unknown IE";
+}
+
+#ifdef DEBUG_SUPPORT
+void showframe(struct ast_iax2_frame *f, struct ast_iax2_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  ",
+		"QUELCH ",
+		"UNQULCH",
+		"POKE",
+		"PAGE",
+		"MWI",
+		"UNSUPPORTED",
+	};
+	char *cmds[] = {
+		"(0?)",
+		"HANGUP ",
+		"RING   ",
+		"RINGING",
+		"ANSWER ",
+		"BUSY   ",
+		"TKOFFHK ",
+		"OFFHOOK" };
+	struct ast_iax2_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->scallno) & 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] -- OSeqno: %4.4d ISeqno: %4.4d Type: %s Subclass: %s\n",
+	(rx ? "Rx" : "Tx"),
+	retries, ntohs(fh->oseqno), ntohs(fh->iseqno), class, subclass);
+		fprintf(stderr,
+"   Timestamp: %05dms  SCall: %5.5d  DCall: %5.5d [%s:%d]\n",
+	ntohl(fh->ts),
+	ntohs(fh->scallno) & ~AST_FLAG_FULL, 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_iax2_pvt *iaxs[AST_IAX2_MAX_CALLS];
+static pthread_mutex_t iaxsl[AST_IAX2_MAX_CALLS];
+
+static int send_command(struct chan_iax2_pvt *, char, int, unsigned int, char *, int, int);
+static int send_command_immediate(struct chan_iax2_pvt *, char, int, unsigned int, char *, int, int);
+static int send_command_final(struct chan_iax2_pvt *, char, int, unsigned int, char *, int, int);
+static int send_command_transfer(struct chan_iax2_pvt *, char, int, unsigned int, char *, int);
+
+static unsigned int calc_timestamp(struct chan_iax2_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]) {
+#ifdef BRIDGE_OPTIMIZATION
+		if (!iaxs[callno]->bridgecallno)
+#endif
+			send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX2_COMMAND_PING, 0, NULL, 0, -1);
+		return 1;
+	} else
+		return 0;
+}
+
+static int send_lagrq(void *data)
+{
+	int callno = (long)data;
+	/* Ping only if it's real not if it's bridged */
+	if (iaxs[callno]) {
+#ifdef BRIDGE_OPTIMIZATION
+		if (!iaxs[callno]->bridgecallno)
+#endif		
+			send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX2_COMMAND_LAGRQ, 0, NULL, 0, -1);
+		return 1;
+	} else
+		return 0;
+}
+
+static unsigned char compress_subclass(int subclass)
+{
+	int x;
+	int power=-1;
+	/* If it's 128 or smaller, just return it */
+	if (subclass < AST_FLAG_SC_LOG)
+		return subclass;
+	/* Otherwise find its power */
+	for (x = 0; x < AST_MAX_SHIFT; x++) {
+		if (subclass & (1 << x)) {
+			if (power > -1) {
+				ast_log(LOG_WARNING, "Can't compress subclass %d\n", subclass);
+				return 0;
+			} else
+				power = x;
+		}
+	}
+	return power | AST_FLAG_SC_LOG;
+}
+
+static int uncompress_subclass(unsigned char csub)
+{
+	/* If the SC_LOG flag is set, return 2^csub otherwise csub */
+	if (csub & AST_FLAG_SC_LOG) {
+		/* special case for 'compressed' -1 */
+		if (csub == 0xff)
+			return -1;
+		else
+			return 1 << (csub & ~AST_FLAG_SC_LOG & AST_MAX_SHIFT);
+	}
+	else
+		return csub;
+}
+
+static struct chan_iax2_pvt *new_iax(void)
+{
+	struct chan_iax2_pvt *tmp;
+	tmp = malloc(sizeof(struct chan_iax2_pvt));
+	if (tmp) {
+		memset(tmp, 0, sizeof(struct chan_iax2_pvt));
+		tmp->callno = 0;
+		tmp->peercallno = 0;
+		tmp->transfercallno = 0;
+		tmp->bridgecallno = 0;
+		tmp->pingid = -1;
+		tmp->lagid = -1;
+		tmp->autoid = -1;
+		tmp->initid = -1;
+		/* strncpy(tmp->context, context, sizeof(tmp->context)-1); */
+		strncpy(tmp->exten, "s", sizeof(tmp->exten)-1);
+	}
+	return tmp;
+}
+
+static int get_samples(struct ast_frame *f)
+{
+	int samples=0;
+	switch(f->subclass) {
+	case AST_FORMAT_G723_1:
+		samples = 240 /* XXX Not necessarily true XXX */;
+		break;
+	case AST_FORMAT_GSM:
+		samples = 160 * (f->datalen / 33);
+		break;
+	case AST_FORMAT_SLINEAR:
+		samples = f->datalen / 2;
+		break;
+	case AST_FORMAT_LPC10:
+		samples = 22 * 8;
+		samples += (((char *)(f->data))[7] & 0x1) * 8;
+		break;
+	case AST_FORMAT_ULAW:
+		samples = f->datalen;
+		break;
+	case AST_FORMAT_ALAW:
+		samples = f->datalen;
+		break;
+	case AST_FORMAT_ADPCM:
+		samples = f->datalen *2;
+		break;
+	default:
+		ast_log(LOG_WARNING, "Don't know how to calculate samples on %d packets\n", f->subclass);
+	}
+	return samples;
+}
+
+static int frames = 0;
+static int iframes = 0;
+static int oframes = 0;
+
+static struct ast_iax2_frame *ast_iax2_frame_new(int direction)
+{
+	struct ast_iax2_frame *fr;
+	fr = malloc(sizeof(struct ast_iax2_frame));
+	if (fr) {
+		fr->direction = direction;
+		fr->retrans = -1;
+		frames++;
+		if (fr->direction == DIRECTION_INGRESS)
+			iframes++;
+		else
+			oframes++;
+	}
+	return fr;
+}
+
+static void ast_iax2_frame_free(struct ast_iax2_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--;
+}
+
+static struct ast_iax2_frame *iaxfrdup2(struct ast_iax2_frame *fr, int ch)
+{
+	/* Malloc() a copy of a frame */
+	struct ast_iax2_frame *new = ast_iax2_frame_new(DIRECTION_INGRESS);
+	if (new) {
+		memcpy(new, fr, sizeof(struct ast_iax2_frame));	
+		new->f = ast_frdup(fr->f);
+		/* Copy full header */
+		if (ch) {
+			memcpy(new->f->data - sizeof(struct ast_iax2_full_hdr),
+					fr->f->data - sizeof(struct ast_iax2_full_hdr), 
+						sizeof(struct ast_iax2_full_hdr));
+			/* Grab new data pointer */
+			new->data = new->f->data - (fr->f->data - fr->data);
+		} else {
+			new->data = NULL;
+			new->datalen = 0;
+		}
+		new->direction = DIRECTION_INGRESS;
+		new->retrans = -1;
+	}
+	return new;
+}
+
+#define NEW_PREVENT 0
+#define NEW_ALLOW 	1
+#define NEW_FORCE 	2
+
+static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short dcallno, struct chan_iax2_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)) {
+			/* 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(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new)
+{
+	int res = -1;
+	int x;
+	int start;
+	if (new <= NEW_ALLOW) {
+		/* Look for an existing connection first */
+		for (x=0;(res < 0) && (x<AST_IAX2_MAX_CALLS);x++) {
+			ast_pthread_mutex_lock(&iaxsl[x]);
+			if (iaxs[x]) {
+				/* Look for an exact match */
+				if (match(sin, callno, dcallno, iaxs[x])) {
+					res = x;
+				}
+			}
+			ast_pthread_mutex_unlock(&iaxsl[x]);
+		}
+	}
+	if ((res < 0) && (new >= NEW_ALLOW)) {
+		/* Create a new one */
+		start = nextcallno;
+		for (x = ((nextcallno + 1) % (AST_IAX2_MAX_CALLS - 1)) + 1; iaxs[x] && (x != start); x = (x + 1) % AST_IAX2_MAX_CALLS)
+		if (x == start) {
+			ast_log(LOG_WARNING, "Unable to accept more calls\n");
+			return -1;
+		}
+		ast_pthread_mutex_lock(&iaxsl[x]);
+		iaxs[x] = new_iax();
+		ast_pthread_mutex_unlock(&iaxsl[x]);
+		if (iaxs[x]) {
+			if (option_debug)
+				ast_log(LOG_DEBUG, "Creating new call structure %d\n", x);
+			iaxs[x]->addr.sin_port = sin->sin_port;
+			iaxs[x]->addr.sin_family = sin->sin_family;
+			iaxs[x]->addr.sin_addr.s_addr = sin->sin_addr.s_addr;
+			iaxs[x]->peercallno = callno;
+			iaxs[x]->callno = x;
+			iaxs[x]->pingtime = DEFAULT_RETRY_TIME;
+			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 iax2_queue_frame(int callno, struct ast_frame *f)
+{
+	int pass =0;
+	/* Assumes lock for callno is already held... */
+	for (;;) {
+		pass++;
+		if (!pthread_mutex_trylock(&iaxsl[callno])) {
+			ast_log(LOG_WARNING, "Lock is not held on pass %d of iax2_queue_frame\n", pass);
+			CRASH;
+		}
+		if (iaxs[callno] && iaxs[callno]->owner) {
+			if (pthread_mutex_trylock(&iaxs[callno]->owner->lock)) {
+				/* Avoid deadlock by pausing and trying again */
+				ast_pthread_mutex_unlock(&iaxsl[callno]);
+				usleep(1);
+				ast_pthread_mutex_lock(&iaxsl[callno]);
+			} else {
+				ast_queue_frame(iaxs[callno]->owner, f, 0);
+				ast_pthread_mutex_unlock(&iaxs[callno]->owner->lock);
+				break;
+			}
+		} else
+			break;
+	}
+	return 0;
+}
+
+static int iax2_send(struct chan_iax2_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 using queueing.  This is called by
+	  the IAX thread with the iaxsl lock held. */
+	struct ast_iax2_frame *fr = data;
+	unsigned int ts;
+	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_IAX2_COMMAND_LAGRQ) {
+				/* If we got a queued request, build a reply and send it */
+				fr->f->subclass = AST_IAX2_COMMAND_LAGRP;
+				iax2_send(iaxs[fr->callno], fr->f, fr->ts, -1, 0, 0, 0);
+			} else if (fr->f->subclass == AST_IAX2_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 {
+			iax2_queue_frame(fr->callno, fr->f);
+		}
+	}
+	/* Free the packet */
+	ast_frfree(fr->f);
+	/* And our iax frame */
+	ast_iax2_frame_free(fr);
+	/* And don't run again */
+	return 0;
+}
+
+static int do_deliver(void *data)
+{
+	/* Locking version of __do_deliver */
+	struct ast_iax2_frame *fr = data;
+	int callno = fr->callno;
+	int res;
+	ast_pthread_mutex_lock(&iaxsl[callno]);
+	res = __do_deliver(data);
+	ast_pthread_mutex_unlock(&iaxsl[callno]);
+	return res;
+}
+
+static int handle_error(void)
+{
+	/* XXX Ideally we should figure out why an error occured and then abort those
+	   rather than continuing to try.  Unfortunately, the published interface does
+	   not seem to work XXX */
+#if 0
+	struct sockaddr_in *sin;
+	int res;
+	struct msghdr m;
+	struct sock_extended_err e;
+	m.msg_name = NULL;
+	m.msg_namelen = 0;
+	m.msg_iov = NULL;
+	m.msg_control = &e;
+	m.msg_controllen = sizeof(e);
+	m.msg_flags = 0;
+	res = recvmsg(netsocket, &m, MSG_ERRQUEUE);
+	if (res < 0)
+		ast_log(LOG_WARNING, "Error detected, but unable to read error: %s\n", strerror(errno));
+	else {
+		if (m.msg_controllen) {
+			sin = (struct sockaddr_in *)SO_EE_OFFENDER(&e);
+			if (sin) 
+				ast_log(LOG_WARNING, "Receive error from %s\n", inet_ntoa(sin->sin_addr));
+			else
+				ast_log(LOG_WARNING, "No address detected??\n");
+		} else {
+			ast_log(LOG_WARNING, "Local error: %s\n", strerror(e.ee_errno));
+		}
+	}
+#endif
+	return 0;
+}
+
+static int send_packet(struct ast_iax2_frame *f)
+{
+	int res;
+	/* Called with iaxsl held */
+	if (option_debug)
+		ast_log(LOG_DEBUG, "Sending %d on %d/%d to %s:%d\n", f->ts, f->callno, iaxs[f->callno]->peercallno, inet_ntoa(iaxs[f->callno]->addr.sin_addr), ntohs(iaxs[f->callno]->addr.sin_port));
+	/* Don't send if there was an error, but return error instead */
+	if (!f->callno) {
+		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;
+	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) {
+		if (option_debug)
+			ast_log(LOG_DEBUG, "Received error: %s\n", strerror(errno));
+		handle_error();
+	} else
+		res = 0;
+	return res;
+}
+
+
+static int iax2_predestroy(int callno)
+{
+	struct ast_channel *c;
+	struct chan_iax2_pvt *pvt;
+	ast_pthread_mutex_lock(&iaxsl[callno]);
+	pvt = iaxs[callno];
+	if (!pvt) {
+		ast_pthread_mutex_unlock(&iaxsl[callno]);
+		return -1;
+	}
+	if (!pvt->alreadygone) {
+		/* No more pings or lagrq's */
+		if (pvt->pingid > -1)
+			ast_sched_del(sched, pvt->pingid);
+		if (pvt->lagid > -1)
+			ast_sched_del(sched, pvt->lagid);
+		if (pvt->autoid > -1)
+			ast_sched_del(sched, pvt->autoid);
+		if (pvt->initid > -1)
+			ast_sched_del(sched, pvt->initid);
+		pvt->pingid = -1;
+		pvt->lagid = -1;
+		pvt->autoid = -1;
+		pvt->initid = -1;
+		pvt->alreadygone = 1;
+	}
+	c = pvt->owner;
+	if (c) {
+		c->_softhangup |= AST_SOFTHANGUP_DEV;
+		c->pvt->pvt = NULL;
+		ast_queue_hangup(c, 0);
+		pvt->owner = NULL;
+		ast_pthread_mutex_lock(&usecnt_lock);
+		usecnt--;
+		if (usecnt < 0) 
+			ast_log(LOG_WARNING, "Usecnt < 0???\n");
+		ast_pthread_mutex_unlock(&usecnt_lock);
+		ast_update_use_count();
+	}
+	ast_pthread_mutex_unlock(&iaxsl[callno]);
+	return 0;
+}
+
+static int iax2_predestroy_nolock(int callno)
+{
+	int res;
+	ast_pthread_mutex_unlock(&iaxsl[callno]);
+	res = iax2_predestroy(callno);
+	ast_pthread_mutex_lock(&iaxsl[callno]);
+	return res;
+}
+
+static void iax2_destroy(int callno)
+{
+	struct chan_iax2_pvt *pvt;
+	struct ast_iax2_frame *cur;
+	struct ast_channel *owner;
+
+retry:
+	ast_pthread_mutex_lock(&iaxsl[callno]);
+	pvt = iaxs[callno];
+	iaxs[callno] = NULL;
+
+	if (pvt)
+		owner = pvt->owner;
+	else
+		owner = NULL;
+	if (owner) {
+		if (pthread_mutex_trylock(&owner->lock)) {
+			ast_log(LOG_NOTICE, "Avoiding IAX destroy deadlock\n");
+			ast_pthread_mutex_unlock(&iaxsl[callno]);
+			usleep(1);
+			goto retry;
+		}
+	}
+	if (pvt) {
+		pvt->owner = NULL;
+		/* No more pings or lagrq's */
+		if (pvt->pingid > -1)
+			ast_sched_del(sched, pvt->pingid);
+		if (pvt->lagid > -1)
+			ast_sched_del(sched, pvt->lagid);
+		if (pvt->autoid > -1)
+			ast_sched_del(sched, pvt->autoid);
+		if (pvt->initid > -1)
+			ast_sched_del(sched, pvt->initid);
+		pvt->pingid = -1;
+		pvt->lagid = -1;
+		pvt->autoid = -1;
+		pvt->initid = -1;
+
+		/* Already gone */
+		pvt->alreadygone = 1;
+
+		if (owner) {
+			/* If there's an owner, prod it to give up */
+			owner->pvt->pvt = NULL;
+			owner->_softhangup |= AST_SOFTHANGUP_DEV;
+			ast_queue_hangup(owner, 0);
+		}
+
+		for (cur = iaxq.head; cur ; cur = cur->next) {
+			/* Cancel any pending transmissions */
+			if (cur->callno == pvt->callno) 
+				cur->retries = -1;
+		}
+		if (pvt->reg) {
+			pvt->reg->callno = 0;
+		}
+		free(pvt);
+	}
+	if (owner) {
+		ast_pthread_mutex_unlock(&owner->lock);
+	}
+	ast_pthread_mutex_unlock(&iaxsl[callno]);
+}
+static void iax2_destroy_nolock(int callno)
+{	
+	/* Actually it's easier to unlock, kill it, and relock */
+	ast_pthread_mutex_unlock(&iaxsl[callno]);
+	iax2_destroy(callno);
+	ast_pthread_mutex_lock(&iaxsl[callno]);
+}
+
+
+
+static int attempt_transmit(void *data)
+{
+	/* Attempt to transmit the frame to the remote peer...
+	   Called without iaxsl held. */
+	struct ast_iax2_frame *f = data;
+	int freeme=0;
+	int callno = f->callno;
+	/* Make sure this call is still active */
+	if (callno) 
+		ast_pthread_mutex_lock(&iaxsl[callno]);
+	if ((f->callno) && 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) {
+					if (f->transfer) {
+						/* Transfer timeout */
+						send_command(iaxs[f->callno], AST_FRAME_IAX, AST_IAX2_COMMAND_TXREJ, 0, NULL, 0, -1);
+					} else if (f->final) {
+						if (f->final) 
+							iax2_destroy_nolock(f->callno);
+					} else {
+						if (iaxs[f->callno]->owner)
+							ast_log(LOG_WARNING, "Max retries exceeded to host %s on %s (type = %d, subclass = %d, ts=%d, seqno=%d)\n", inet_ntoa(iaxs[f->callno]->addr.sin_addr),iaxs[f->callno]->owner->name , f->f->frametype, f->f->subclass, f->ts, f->oseqno);
+						iaxs[f->callno]->error = ETIMEDOUT;
+						if (iaxs[f->callno]->owner) {
+							struct ast_frame fr = { 0, };
+							/* Hangup the fd */
+							fr.frametype = AST_FRAME_CONTROL;
+							fr.subclass = AST_CONTROL_HANGUP;
+							iax2_queue_frame(f->callno, &fr);
+						} else {
+							if (iaxs[f->callno]->reg) {
+								memset(&iaxs[f->callno]->reg->us, 0, sizeof(iaxs[f->callno]->reg->us));
+								iaxs[f->callno]->reg->regstate = REG_STATE_TIMEOUT;
+								iaxs[f->callno]->reg->refresh = AST_DEFAULT_REG_EXPIRE;
+							}
+							iax2_destroy_nolock(f->callno);
+						}
+					}
+
+				}
+				freeme++;
+		} else {
+			/* Attempt transmission */
+			send_packet(f);
+			f->retries++;
+			/* Try again later after 10 times as long */
+			f->retrytime *= 10;
+			if (f->retrytime > MAX_RETRY_TIME)
+				f->retrytime = MAX_RETRY_TIME;
+			/* 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++;
+	}
+	if (callno)
+		ast_pthread_mutex_unlock(&iaxsl[callno]);
+	/* Do not try again */
+	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_iax2_frame_free(f);
+	}
+	return 0;
+}
+
+static int iax2_set_jitter(int fd, int argc, char *argv[])
+{
+	if ((argc != 4) && (argc != 5))
+		return RESULT_SHOWUSAGE;
+	if (argc == 4) {
+		max_jitter_buffer = atoi(argv[3]);
+		if (max_jitter_buffer < 0)
+			max_jitter_buffer = 0;
+	} else {
+		if (argc == 5) {
+			if ((atoi(argv[3]) >= 0) && (atoi(argv[3]) < AST_IAX2_MAX_CALLS)) {
+				if (iaxs[atoi(argv[3])]) {
+					iaxs[atoi(argv[3])]->jitterbuffer = atoi(argv[4]);
+					if (iaxs[atoi(argv[3])]->jitterbuffer < 0)
+						iaxs[atoi(argv[3])]->jitterbuffer = 0;
+				} else
+					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]));
+		}
+	}
+	return RESULT_SUCCESS;
+}
+
+static char jitter_usage[] = 
+"Usage: iax set jitter [callid] <value>\n"
+"       If used with a callid, it sets the jitter buffer to the given static\n"
+"value (until its next calculation).  If used without a callid, the value is used\n"
+"to establish the maximum excess jitter buffer that is permitted before the jitter\n"
+"buffer size is reduced.";
+
+static int iax2_show_stats(int fd, int argc, char *argv[])
+{
+	struct ast_iax2_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 iax2_show_cache(int fd, int argc, char *argv[])
+{
+	struct iax2_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_MATCHMORE)
+			strcat(tmp, "MATCHMORE|");
+		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 = 
+{ { "iax2", "set", "jitter", NULL }, iax2_set_jitter, "Sets IAX jitter buffer", jitter_usage };
+
+static struct ast_cli_entry cli_show_stats =
+{ { "iax2", "show", "stats", NULL }, iax2_show_stats, "Display IAX statistics", show_stats_usage };
+
+static struct ast_cli_entry cli_show_cache =
+{ { "iax2", "show", "cache", NULL }, iax2_show_cache, "Display IAX cached dialplan", show_cache_usage };
+
+static unsigned int calc_rxstamp(struct chan_iax2_pvt *p);
+
+#ifdef BRIDGE_OPTIMIZATION
+static unsigned int calc_fakestamp(struct chan_iax2_pvt *from, struct chan_iax2_pvt *to, unsigned int ts);
+
+static int forward_delivery(struct ast_iax2_frame *fr)
+{
+	struct chan_iax2_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 iax2_send(p2, fr->f, fr->ts, -1, 0, 0, 0);
+}
+#endif
+
+static int schedule_delivery(struct ast_iax2_frame *fr, int reallydeliver)
+{
+	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.  Called by
+	   IAX thread, with iaxsl lock held. */
+	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 */
+	for (x=0;x<MEMORY_SIZE - 1;x++) 
+		iaxs[fr->callno]->history[x] = iaxs[fr->callno]->history[x+1];
+	/* Add a history entry for this one */
+	iaxs[fr->callno]->history[x] = ms;
+
+	/* Initialize the minimum to reasonable values.  It's too much
+	   work to do the same for the maximum, repeatedly */
+	min=iaxs[fr->callno]->history[0];
+	for (z=0;z < iax2_dropcount + 1;z++) {
+		/* Start very optimistic ;-) */
+		max=-999999999;
+		for (x=0;x<MEMORY_SIZE;x++) {
+			if (max < iaxs[fr->callno]->history[x]) {
+				/* We have a candidate new maximum value.  Make
+				   sure it's not in our drop list */
+				match = 0;
+				for (y=0;!match && (y<z);y++)
+					match |= (drops[y] == x);
+				if (!match) {
+					/* It's not in our list, use it as the new maximum */
+					max = iaxs[fr->callno]->history[x];
+					maxone = x;
+				}
+				
+			}
+			if (!z) {
+				/* On our first pass, find the minimum too */
+				if (min > iaxs[fr->callno]->history[x])
+					min = iaxs[fr->callno]->history[x];
+			}
+		}
+#if 1
+		drops[z] = maxone;
+#endif
+	}
+	/* Just for reference, keep the "jitter" value, the difference between the
+	   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) {
+		if (option_debug)
+			ast_log(LOG_DEBUG, "Constraining buffer from %d to %d + %d\n", max, min , maxjitterbuffer);
+		max = min + maxjitterbuffer;
+	}
+#endif
+
+	/* If our jitter buffer is smaller than our maximum delay, grow the jitter
+	   buffer immediately to accomodate it (and a little more).  */
+	if (max > iaxs[fr->callno]->jitterbuffer)
+		iaxs[fr->callno]->jitterbuffer = max 
+			/* + ((float)iaxs[fr->callno]->jitter) * 0.1 */;
+		
+
+	if (option_debug)
+		ast_log(LOG_DEBUG, "min = %d, max = %d, jb = %d, lateness = %d\n", min, max, iaxs[fr->callno]->jitterbuffer, ms);
+	
+	/* Subtract the lateness from our jitter buffer to know how long to wait
+	   before sending our packet.  */
+	ms = iaxs[fr->callno]->jitterbuffer - ms;
+	
+	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 {
+			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 */
+			ast_iax2_frame_free(fr);
+		}
+	} else {
+		if (option_debug)
+			ast_log(LOG_DEBUG, "Scheduling delivery in %d ms\n", ms);
+		fr->retrans = ast_sched_add(sched, ms, do_deliver, fr);
+	}
+	return 0;
+}
+
+static int iax2_transmit(struct ast_iax2_frame *fr)
+{
+	/* Lock the queue and place this packet at the end */
+	fr->next = NULL;
+	fr->prev = NULL;
+	/* By setting this to 0, the network thread will send it for us, and
+	   queue retransmission if necessary */
+	fr->sentyet = 0;
+	ast_pthread_mutex_lock(&iaxq.lock);
+	if (!iaxq.head) {
+		/* Empty queue */
+		iaxq.head = fr;
+		iaxq.tail = fr;
+	} else {
+		/* Double link */
+		iaxq.tail->next = fr;
+		fr->prev = iaxq.tail;
+		iaxq.tail = fr;
+	}
+	iaxq.count++;
+	ast_pthread_mutex_unlock(&iaxq.lock);
+	/* Wake up the network thread */
+	pthread_kill(netthreadid, SIGURG);
+	return 0;
+}
+
+
+
+static int iax2_digit(struct ast_channel *c, char digit)
+{
+	return send_command(c->pvt->pvt, AST_FRAME_DTMF, digit, 0, NULL, 0, -1);
+}
+
+static int iax2_sendtext(struct ast_channel *c, char *text)
+{
+	
+	return send_command(c->pvt->pvt, AST_FRAME_TEXT,
+		0, 0, text, strlen(text) + 1, -1);
+}
+
+static int iax2_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 iax2_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 iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan)
+{
+	struct chan_iax2_pvt *pvt = newchan->pvt->pvt;
+	pvt->owner = newchan;
+	return 0;
+}
+
+static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, int *maxtime, char *peer, char *context)
+{
+	struct hostent *hp;
+	struct iax2_peer *p;
+	int found=0;
+	if (sendani)
+		*sendani = 0;
+	if (maxtime)
+		*maxtime = 0;
+	sin->sin_family = AF_INET;
+	ast_pthread_mutex_lock(&peerl.lock);
+	p = peerl.peers;
+	while(p) {
+		if (!strcasecmp(p->name, peer)) {
+			found++;
+			if (capability)
+				*capability = p->capability;
+			if ((p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) &&
+				(!p->maxms || ((p->lastms > 0)  && (p->lastms <= p->maxms)))) {
+				if (sendani)
+					*sendani = p->sendani;		/* Whether we transmit ANI */
+				if (maxtime)
+					*maxtime = p->maxms;		/* Max time they should take */
+				if (context)
+					strncpy(context, p->context, AST_MAX_EXTENSION - 1);
+				if (p->addr.sin_addr.s_addr) {
+					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;
+	}
+	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 {
+			ast_log(LOG_WARNING, "No such host: %s\n", peer);
+			return -1;
+		}
+	} else if (!p)
+		return -1;
+	else
+		return 0;
+}
+
+static int auto_congest(void *nothing)
+{
+	int callno = (int)(long)(nothing);
+	struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_CONGESTION };
+	ast_pthread_mutex_lock(&iaxsl[callno]);
+	if (iaxs[callno]) {
+		iaxs[callno]->initid = -1;
+		iax2_queue_frame(callno, &f);
+		ast_log(LOG_NOTICE, "Auto-congesting call due to slow response\n");
+	}
+	ast_pthread_mutex_unlock(&iaxsl[callno]);
+	return 0;
+}
+
+static int iax_ie_append_raw(struct iax_ie_data *ied, unsigned char ie, void *data, int datalen)
+{
+	if (datalen > (sizeof(ied->buf) - ied->pos)) {
+		ast_log(LOG_WARNING, "Out of space for ie '%s' (%d), need %d have %d\n", ie2str(ie), ie, datalen, sizeof(ied->buf) - ied->pos);
+		return -1;
+	}
+	ied->buf[ied->pos++] = ie;
+	ied->buf[ied->pos++] = datalen;
+	memcpy(ied->buf + ied->pos, data, datalen);
+	ied->pos += datalen;
+	return 0;
+}
+
+static int iax_ie_append_addr(struct iax_ie_data *ied, unsigned char ie, struct sockaddr_in *sin)
+{
+	return iax_ie_append_raw(ied, ie, sin, sizeof(struct sockaddr_in));
+}
+
+static int iax_ie_append_int(struct iax_ie_data *ied, unsigned char ie, unsigned int value) 
+{
+	unsigned int newval;
+	newval = htonl(value);
+	return iax_ie_append_raw(ied, ie, &newval, sizeof(newval));
+}
+
+static int iax_ie_append_short(struct iax_ie_data *ied, unsigned char ie, unsigned short value) 
+{
+	unsigned short newval;
+	newval = htons(value);
+	return iax_ie_append_raw(ied, ie, &newval, sizeof(newval));
+}
+
+static int iax_ie_append_str(struct iax_ie_data *ied, unsigned char ie, unsigned char *str)
+{
+	return iax_ie_append_raw(ied, ie, str, strlen(str));
+}
+
+static int iax2_call(struct ast_channel *c, char *dest, int timeout)
+{
+	struct sockaddr_in sin;
+	char host[256];
+	char *rdest;
+	char *rcontext;
+	char *username;
+	char *secret = NULL;
+	char *hname;
+	char cid[256] = "";
+	char *l=NULL, *n=NULL;
+	struct iax_ie_data ied;
+	char myrdest [5] = "s";
+	char context[AST_MAX_EXTENSION] ="";
+	char *portno = NULL;
+	struct chan_iax2_pvt *p = c->pvt->pvt;
+	char *stringp=NULL;
+	if ((c->_state != AST_STATE_DOWN) && (c->_state != AST_STATE_RESERVED)) {
+		ast_log(LOG_WARNING, "Line is already in use (%s)?\n", c->name);
+		return -1;
+	}
+	strncpy(host, dest, sizeof(host)-1);
+	stringp=host;
+	strsep(&stringp, "/");
+	/* If no destination extension specified, use 's' */
+	rdest = strsep(&stringp, "/");
+	if (!rdest) 
+		rdest = myrdest;
+	stringp=rdest;
+	strsep(&stringp, "@");
+	rcontext = strsep(&stringp, "@");
+	stringp=host;
+	strsep(&stringp, "@");
+	username = strsep(&stringp, "@");
+	if (username) {
+		/* Really the second argument is the host, not the username */
+		hname = username;
+		username = host;
+	} else {
+		hname = host;
+	}
+	if (username) {
+		stringp=username;
+		username = strsep(&stringp, ":");
+		secret = strsep(&stringp, ":");
+	}
+	stringp=hname;
+	if (strsep(&stringp, ":")) {
+		stringp=hname;
+		strsep(&stringp, ":");
+		portno = strsep(&stringp, ":");
+	}
+	if (create_addr(&sin, NULL, 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));
+	}
+	if (c->callerid) {
+		strncpy(cid, c->callerid, sizeof(cid) - 1);
+		ast_callerid_parse(cid, &n, &l);
+		if (l)
+			ast_shrink_phone_number(l);
+	}
+	/* Now build request */	
+	memset(&ied, 0, sizeof(ied));
+	/* On new call, first IE MUST be IAX version of caller */
+	iax_ie_append_short(&ied, IAX_IE_VERSION, AST_IAX2_PROTO_VERSION);
+	iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, rdest);
+	if (l)
+		iax_ie_append_str(&ied, IAX_IE_CALLING_NUMBER, l);
+	if (n)
+		iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, n);
+	if (p->sendani && c->ani) {
+		l = n = NULL;
+		strncpy(cid, c->ani, sizeof(cid) - 1);
+		ast_callerid_parse(cid, &n, &l);
+		if (l) {
+			ast_shrink_phone_number(l);
+			iax_ie_append_str(&ied, IAX_IE_CALLING_ANI, l);
+		}
+	}
+	if (c->language && strlen(c->language))
+		iax_ie_append_str(&ied, IAX_IE_LANGUAGE, c->language);
+	if (c->dnid && strlen(c->dnid))
+		iax_ie_append_str(&ied, IAX_IE_DNID, c->dnid);
+	if (rcontext)
+		iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, rcontext);
+	if (username)
+		iax_ie_append_str(&ied, IAX_IE_USERNAME, username);
+	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);
+	}
+	iax_ie_append_int(&ied, IAX_IE_FORMAT, c->nativeformats);
+	iax_ie_append_int(&ied, IAX_IE_CAPABILITY, p->capability);
+	iax_ie_append_int(&ied, IAX_IE_ADSICPE, c->adsicpe);
+	/* Transmit the string in a "NEW" request */
+#if 0
+	/* XXX We have no equivalent XXX */
+	if (option_verbose > 2)
+		ast_verbose(VERBOSE_PREFIX_3 "Calling using options '%s'\n", requeststr);
+#endif		
+	if (p->maxtime) {
+		/* Initialize pingtime and auto-congest time */
+		p->pingtime = p->maxtime / 2;
+		p->initid = ast_sched_add(sched, p->maxtime * 2, auto_congest, (void *)(long)p->callno);
+	}
+	send_command(p, AST_FRAME_IAX,
+		AST_IAX2_COMMAND_NEW, 0, ied.buf, ied.pos, -1);
+	ast_setstate(c, AST_STATE_RINGING);
+	return 0;
+}
+
+static int iax2_hangup(struct ast_channel *c) 
+{
+	struct chan_iax2_pvt *pvt = c->pvt->pvt;
+	int alreadygone;
+	int callno;
+	if (pvt) {
+		callno = pvt->callno;
+		ast_pthread_mutex_lock(&iaxsl[callno]);
+		ast_log(LOG_DEBUG, "We're hanging up %s now...\n", c->name);
+		alreadygone = pvt->alreadygone;
+		/* Send the hangup unless we have had a transmission error or are already gone */
+		if (!pvt->error && !alreadygone) 
+			send_command_final(pvt, AST_FRAME_IAX, AST_IAX2_COMMAND_HANGUP, 0, NULL, 0, -1);
+		/* Explicitly predestroy it */
+		iax2_predestroy_nolock(callno);
+		/* If we were already gone to begin with, destroy us now */
+		if (alreadygone) {
+			ast_log(LOG_DEBUG, "Really destroying %s now...\n", c->name);
+			iax2_destroy_nolock(callno);
+		}
+		ast_pthread_mutex_unlock(&iaxsl[callno]);
+	}
+	if (option_verbose > 2) 
+		ast_verbose(VERBOSE_PREFIX_3 "Hungup '%s'\n", c->name);
+	return 0;
+}
+
+static int iax2_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_iax2_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 *iax2_read(struct ast_channel *c) 
+{
+	static struct ast_frame f = { AST_FRAME_NULL, };
+	ast_log(LOG_NOTICE, "I should never be called!\n");
+	return &f;
+}
+
+static int iax2_start_transfer(struct ast_channel *c0, struct ast_channel *c1)
+{
+	int res;
+	struct iax_ie_data ied0;
+	struct iax_ie_data ied1;
+	struct chan_iax2_pvt *p0 = c0->pvt->pvt;
+	struct chan_iax2_pvt *p1 = c1->pvt->pvt;
+	memset(&ied0, 0, sizeof(ied0));
+	iax_ie_append_addr(&ied0, IAX_IE_APPARENT_ADDR, &p1->addr);
+	iax_ie_append_short(&ied0, IAX_IE_CALLNO, p1->peercallno);
+
+	memset(&ied1, 0, sizeof(ied1));
+	iax_ie_append_addr(&ied1, IAX_IE_APPARENT_ADDR, &p0->addr);
+	iax_ie_append_short(&ied1, IAX_IE_CALLNO, p0->peercallno);
+	
+	res = send_command(p0, AST_FRAME_IAX, AST_IAX2_COMMAND_TXREQ, 0, ied0.buf, ied0.pos, -1);
+	if (res)
+		return -1;
+	res = send_command(p1, AST_FRAME_IAX, AST_IAX2_COMMAND_TXREQ, 0, ied1.buf, ied1.pos, -1);
+	if (res)
+		return -1;
+	p0->transferring = TRANSFER_BEGIN;
+	p1->transferring = TRANSFER_BEGIN;
+	return 0;
+}
+
+static int iax2_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_iax2_pvt *p0 = c0->pvt->pvt;
+	struct chan_iax2_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 (c0->nativeformats != c1->nativeformats) {
+			ast_verbose(VERBOSE_PREFIX_3 "Operating with different codecs, can't native bridge...\n");
+			return -2;
+		}
+		if (!transferstarted) {
+			/* Try the transfer */
+			if (iax2_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 = 0;
+	p1->bridgecallno = 0;
+	return res;
+}
+
+static int iax2_answer(struct ast_channel *c)
+{
+	struct chan_iax2_pvt *pvt = c->pvt->pvt;
+	if (option_debug)
+		ast_log(LOG_DEBUG, "Answering\n");
+	return send_command(pvt, AST_FRAME_CONTROL, AST_CONTROL_ANSWER, 0, NULL, 0, -1);
+}
+
+static int iax2_indicate(struct ast_channel *c, int condition)
+{
+	struct chan_iax2_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 iax2_write(struct ast_channel *c, struct ast_frame *f);
+
+static int iax2_getpeername(struct sockaddr_in sin, char *host, int len)
+{
+	struct iax2_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_iax2_new(struct chan_iax2_pvt *i, int state, int capability)
+{
+	char host[256];
+	struct ast_channel *tmp;
+	tmp = ast_channel_alloc(1);
+	if (tmp) {
+		if (!iax2_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), "IAX2[%s@%s]/%d", i->username, host, i->callno);
+		else
+			snprintf(tmp->name, sizeof(tmp->name), "IAX2[%s]/%d", host, i->callno);
+		tmp->type = type;
+		/* We can support any format by default, until we get restricted */
+		tmp->nativeformats = capability;
+		tmp->readformat = 0;
+		tmp->writeformat = 0;
+		tmp->pvt->pvt = i;
+		tmp->pvt->send_digit = iax2_digit;
+		tmp->pvt->send_text = iax2_sendtext;
+		tmp->pvt->send_image = iax2_sendimage;
+		tmp->pvt->send_html = iax2_sendhtml;
+		tmp->pvt->call = iax2_call;
+		tmp->pvt->hangup = iax2_hangup;
+		tmp->pvt->answer = iax2_answer;
+		tmp->pvt->read = iax2_read;
+		tmp->pvt->write = iax2_write;
+		tmp->pvt->indicate = iax2_indicate;
+		tmp->pvt->setoption = iax2_setoption;
+		tmp->pvt->bridge = iax2_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);
+		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 = iax2_fixup;
+		i->owner = tmp;
+		i->capability = capability;
+		ast_setstate(tmp, state);
+		ast_pthread_mutex_lock(&usecnt_lock);
+		usecnt++;
+		ast_pthread_mutex_unlock(&usecnt_lock);
+		ast_update_use_count();
+		if (state != AST_STATE_DOWN) {
+			if (ast_pbx_start(tmp)) {
+				ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+				ast_hangup(tmp);
+				tmp = NULL;
+			}
+		}
+	}
+	return tmp;
+}
+
+static unsigned int calc_timestamp(struct chan_iax2_pvt *p, unsigned int ts)
+{
+	struct timeval tv;
+	unsigned int ms;
+	if (!p->offset.tv_sec && !p->offset.tv_usec)
+		gettimeofday(&p->offset, NULL);
+	/* If the timestamp is specified, just send it as is */
+	if (ts)
+		return ts;
+	gettimeofday(&tv, NULL);
+	ms = (tv.tv_sec - p->offset.tv_sec) * 1000 + (tv.tv_usec - p->offset.tv_usec) / 1000;
+	/* We never send the same timestamp twice, so fudge a little if we must */
+	if (ms <= p->lastsent)
+		ms = p->lastsent + 1;
+	p->lastsent = ms;
+	return ms;
+}
+
+#ifdef BRIDGE_OPTIMIZATION
+static unsigned int calc_fakestamp(struct chan_iax2_pvt *p1, struct chan_iax2_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_iax2_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->rxcore.tv_sec) * 1000 + (tv.tv_usec - p->rxcore.tv_usec) / 1000;
+	return ms;
+}
+
+static int iax2_send(struct chan_iax2_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.  Send immediately without retransmission
+	   or delayed, with retransmission */
+	struct ast_iax2_full_hdr *fh;
+	struct ast_iax2_mini_hdr *mh;
+	struct ast_iax2_frame *fr, fr2;
+	int res;
+	unsigned int lastsent;
+	/* Allocate an ast_iax2_frame */
+	if (now)
+		fr = &fr2;
+	else
+		fr = ast_iax2_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);
+		if (!now)
+			ast_iax2_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");
+		if (!now)
+			ast_iax2_frame_free(fr);
+		return -1;
+	}
+	if (fr->f->offset < sizeof(struct ast_iax2_full_hdr)) {
+		ast_log(LOG_WARNING, "Packet from '%s' not friendly\n", fr->f->src);
+		free(fr);
+		return -1;
+	}
+	lastsent = pvt->lastsent;
+	fr->ts = calc_timestamp(pvt, ts);
+	if (!fr->ts) {
+		ast_log(LOG_WARNING, "timestamp is 0?\n");
+		if (!now)
+			ast_iax2_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) 
+		/* or not a voice frame */ || 
+		(fr->f->subclass != pvt->svoiceformat) 
+		/* or new voice format */ ) {
+		/* We need a full frame */
+		if (seqno > -1)
+			fr->oseqno = seqno;
+		else
+			fr->oseqno = pvt->oseqno++;
+		fr->iseqno = pvt->iseqno;
+		fh = (struct ast_iax2_full_hdr *)(fr->f->data - sizeof(struct ast_iax2_full_hdr));
+		fh->scallno = htons(fr->callno | AST_FLAG_FULL);
+		fh->ts = htonl(fr->ts);
+		fh->oseqno = htons(fr->oseqno);
+		fh->iseqno = htons(fr->iseqno);
+		/* Keep track of the last thing we've acknowledged */
+		pvt->aseqno = fr->iseqno;
+		fh->type = fr->f->frametype & 0xFF;
+		fh->csub = compress_subclass(fr->f->subclass);
+		if (transfer) {
+			fh->dcallno = htons(pvt->transfercallno);
+		} else
+			fh->dcallno = htons(pvt->peercallno);
+		fr->datalen = fr->f->datalen + sizeof(struct ast_iax2_full_hdr);
+		fr->data = fh;
+		fr->retries = 0;
+		/* Retry after 2x the ping time has passed */
+		fr->retrytime = pvt->pingtime * 2;
+		if (fr->retrytime < MIN_RETRY_TIME)
+			fr->retrytime = MIN_RETRY_TIME;
+		if (fr->retrytime > MAX_RETRY_TIME)
+			fr->retrytime = MAX_RETRY_TIME;
+		/* Acks' don't get retried */
+		if ((f->frametype == AST_FRAME_IAX) && (f->subclass == AST_IAX2_COMMAND_ACK))
+			fr->retries = -1;
+		if (f->frametype == AST_FRAME_VOICE) {
+			pvt->svoiceformat = f->subclass;
+		}
+		if (now) {
+			res = send_packet(fr);
+			ast_frfree(fr->f);
+		} else
+			res = iax2_transmit(fr);
+	} else {
+		/* Mini-frames have no sequence number */
+		fr->oseqno = -1;
+		fr->iseqno = -1;
+		/* Mini frame will do */
+		mh = (struct ast_iax2_mini_hdr *)(fr->f->data - sizeof(struct ast_iax2_mini_hdr));
+		mh->callno = htons(fr->callno);
+		mh->ts = htons(fr->ts & 0xFFFF);
+		fr->datalen = fr->f->datalen + sizeof(struct ast_iax2_mini_hdr);
+		fr->data = mh;
+		fr->retries = -1;
+		if (now) {
+			res = send_packet(fr);
+			ast_frfree(fr->f);
+		} else
+			res = iax2_transmit(fr);
+	}
+	return res;
+}
+
+
+
+static int iax2_show_users(int fd, int argc, char *argv[])
+{
+#define FORMAT "%-15.15s  %-15.15s  %-15.15s  %-15.15s  %-5.5s\n"
+#define FORMAT2 "%-15.15s  %-15.15s  %-15.15d  %-15.15s  %-5.5s\n"
+	struct iax2_user *user;
+	if (argc != 3) 
+		return RESULT_SHOWUSAGE;
+	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, FORMAT2, user->name, user->secret, user->authmethods, 
+				user->contexts ? user->contexts->context : context,
+				user->ha ? "Yes" : "No");
+	}
+	ast_pthread_mutex_unlock(&userl.lock);
+	return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static int iax2_show_peers(int fd, int argc, char *argv[])
+{
+#define FORMAT2 "%-15.15s  %-15.15s %s  %-15.15s  %-8s  %-10s\n"
+#define FORMAT "%-15.15s  %-15.15s %s  %-15.15s  %-8d  %-10s\n"
+	struct iax2_peer *peer;
+	char name[256] = "";
+	if (argc != 3)
+		return RESULT_SHOWUSAGE;
+	ast_pthread_mutex_lock(&peerl.lock);
+	ast_cli(fd, FORMAT2, "Name/Username", "Host", "   ", "Mask", "Port", "Status");
+	for (peer = peerl.peers;peer;peer = peer->next) {
+		char nm[20];
+		char status[20];
+		if (strlen(peer->username))
+			snprintf(name, sizeof(name), "%s/%s", peer->name, peer->username);
+		else
+			strncpy(name, peer->name, sizeof(name) - 1);
+		if (peer->maxms) {
+			if (peer->lastms < 0)
+				strcpy(status, "UNREACHABLE");
+			else if (peer->lastms > peer->maxms) 
+				snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
+			else if (peer->lastms) 
+				snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
+			else 
+				strcpy(status, "UNKNOWN");
+		} else 
+			strcpy(status, "Unmonitored");
+		strncpy(nm, inet_ntoa(peer->mask), sizeof(nm)-1);
+		ast_cli(fd, FORMAT, name, 
+					peer->addr.sin_addr.s_addr ? inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
+					peer->dynamic ? "(D)" : "(S)",
+					nm,
+					ntohs(peer->addr.sin_port), status);
+	}
+	ast_pthread_mutex_unlock(&peerl.lock);
+	return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+/* JDG: callback to display iax peers in manager */
+static int manager_iax2_show_peers( struct mansession *s, struct message *m )
+{
+	char *a[] = { "iax2", "show", "users" };
+	int ret;
+	ret = iax2_show_peers( s->fd, 3, a );
+	ast_cli( s->fd, "\r\n" );
+	return ret;
+} /* /JDG */
+
+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 iax2_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 iax2_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
+}
+
+static int iax2_show_channels(int fd, int argc, char *argv[])
+{
+#define FORMAT2 "%-15.15s  %-10.10s  %-11.11s  %-11.11s  %-7.7s  %-6.6s  %s\n"
+#define FORMAT  "%-15.15s  %-10.10s  %5.5d/%5.5d  %5.5d/%5.5d  %-5.5dms  %-4.4dms  %d\n"
+	int x;
+	int numchans = 0;
+	if (argc != 3)
+		return RESULT_SHOWUSAGE;
+	ast_cli(fd, FORMAT2, "Peer", "Username", "ID (Lo/Rem)", "Seq (Tx/Rx)", "Lag", "Jitter", "Format");
+	for (x=0;x<AST_IAX2_MAX_CALLS;x++) {
+		ast_pthread_mutex_lock(&iaxsl[x]);
+		if (iaxs[x]) {
+			ast_cli(fd, FORMAT, inet_ntoa(iaxs[x]->addr.sin_addr), 
+						strlen(iaxs[x]->username) ? iaxs[x]->username : "(None)", 
+						iaxs[x]->callno, iaxs[x]->peercallno, 
+						iaxs[x]->oseqno, iaxs[x]->iseqno, 
+						iaxs[x]->lag,
+						iaxs[x]->jitter,
+						iaxs[x]->voiceformat);
+			numchans++;
+		}
+		ast_pthread_mutex_unlock(&iaxsl[x]);
+	}
+	ast_cli(fd, "%d active IAX channel(s)\n", numchans);
+	return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static int iax2_do_debug(int fd, int argc, char *argv[])
+{
+	if (argc != 2)
+		return RESULT_SHOWUSAGE;
+	iaxdebug = 1;
+	ast_cli(fd, "IAX2 Debugging Enabled\n");
+	return RESULT_SUCCESS;
+}
+
+static int iax2_no_debug(int fd, int argc, char *argv[])
+{
+	if (argc != 3)
+		return RESULT_SHOWUSAGE;
+	iaxdebug = 0;
+	ast_cli(fd, "IAX2 Debugging Disabled\n");
+	return RESULT_SUCCESS;
+}
+
+
+
+static char show_users_usage[] = 
+"Usage: iax2 show users\n"
+"       Lists all users known to the IAX2 (Inter-Asterisk eXchange rev 2) subsystem.\n";
+
+static char show_channels_usage[] = 
+"Usage: iax2 show channels\n"
+"       Lists all currently active IAX2 channels.\n";
+
+static char show_peers_usage[] = 
+"Usage: iax2 show peers\n"
+"       Lists all known IAX peers.\n";
+
+static char show_reg_usage[] =
+"Usage: iax2 show registry\n"
+"       Lists all registration requests and status.\n";
+
+#ifdef DEBUG_SUPPORT
+
+static char debug_usage[] = 
+"Usage: iax2 debug\n"
+"       Enables dumping of IAX2 packets for debugging purposes\n";
+
+static char no_debug_usage[] = 
+"Usage: iax2 no debug\n"
+"       Disables dumping of IAX2 packets for debugging purposes\n";
+
+#endif
+
+static struct ast_cli_entry  cli_show_users = 
+	{ { "iax2", "show", "users", NULL }, iax2_show_users, "Show defined IAX2 users", show_users_usage };
+static struct ast_cli_entry  cli_show_channels =
+	{ { "iax2", "show", "channels", NULL }, iax2_show_channels, "Show active IAX2 channels", show_channels_usage };
+static struct ast_cli_entry  cli_show_peers =
+	{ { "iax2", "show", "peers", NULL }, iax2_show_peers, "Show defined IAX2 peers", show_peers_usage };
+static struct ast_cli_entry  cli_show_registry =
+	{ { "iax2", "show", "registry", NULL }, iax2_show_registry, "Show IAX2 registration status", show_reg_usage };
+static struct ast_cli_entry  cli_debug =
+	{ { "iax2", "debug", NULL }, iax2_do_debug, "Enable IAX2 debugging", debug_usage };
+static struct ast_cli_entry  cli_no_debug =
+	{ { "iax2", "no", "debug", NULL }, iax2_no_debug, "Disable IAX2 debugging", no_debug_usage };
+
+static int iax2_write(struct ast_channel *c, struct ast_frame *f)
+{
+	struct chan_iax2_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 iax2_send(i, f, 0, -1, 0, 0, 0);
+}
+
+static int __send_command(struct chan_iax2_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;
+	f.subclass = command;
+	f.datalen = datalen;
+	f.samples = 0;
+	f.mallocd = 0;
+	f.offset = 0;
+	f.src = __FUNCTION__;
+	f.data = data;
+	return iax2_send(i, &f, ts, seqno, now, transfer, final);
+}
+
+static int send_command(struct chan_iax2_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_iax2_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_iax2_pvt *i, char type, int command, unsigned int ts, char *data, int datalen, int seqno)
+{
+	/* It is assumed that the callno has already been locked */
+	iax2_predestroy_nolock(i->callno);
+	return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 1);
+}
+
+static int send_command_immediate(struct chan_iax2_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_iax2_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 iax2_context *con, char *context)
+{
+	while(con) {
+		if (!strcmp(con->context, context))
+			return -1;
+		con = con->next;
+	}
+	return 0;
+}
+
+
+static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies)
+{
+	/* Start pessimistic */
+	int res = -1;
+	int version = 2;
+	struct iax2_user *user;
+	int gotcapability=0;
+	if (!iaxs[callno])
+		return res;
+	if (ies->called_number)
+		strncpy(iaxs[callno]->exten, ies->called_number, sizeof(iaxs[callno]->exten) - 1);
+	if (ies->calling_number) {
+		if (ies->calling_name)
+			snprintf(iaxs[callno]->callerid, sizeof(iaxs[callno]->callerid), "\"%s\" <%s>", ies->calling_name, ies->calling_number);
+		else
+			strncpy(iaxs[callno]->callerid, ies->calling_number, sizeof(iaxs[callno]->callerid) - 1);
+	} else if (ies->calling_name)
+		strncpy(iaxs[callno]->callerid, ies->calling_name, sizeof(iaxs[callno]->callerid) - 1);
+	if (ies->calling_ani)
+		strncpy(iaxs[callno]->ani, ies->calling_ani, sizeof(iaxs[callno]->ani) - 1);
+	if (ies->dnid)
+		strncpy(iaxs[callno]->dnid, ies->dnid, sizeof(iaxs[callno]->dnid)-1);
+	if (ies->called_context)
+		strncpy(iaxs[callno]->context, ies->called_context, sizeof(iaxs[callno]->context)-1);
+	if (ies->language)
+		strncpy(iaxs[callno]->language, ies->language, sizeof(iaxs[callno]->language)-1);
+	if (ies->username)
+		strncpy(iaxs[callno]->username, ies->username, sizeof(iaxs[callno]->username)-1);
+	if (ies->format)
+		iaxs[callno]->peerformat = ies->format;
+	if (ies->adsicpe)
+		iaxs[callno]->peeradsicpe = ies->adsicpe;
+	if (ies->capability) {
+		gotcapability = 1;
+		iaxs[callno]->peercapability = ies->capability;
+	} 
+	if (ies->version)
+		version = ies->version;
+	if (!gotcapability) 
+		iaxs[callno]->peercapability = iaxs[callno]->peerformat;
+	if (version > AST_IAX2_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;
+	}
+	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 */
+			&& 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)-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)-1);
+				else
+					strncpy(iaxs[callno]->context, context, sizeof(iaxs[callno]->context)-1);
+			}
+			/* Copy the 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 */
+			iaxs[callno]->authmethods = user->authmethods;
+			/* 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;	
+	}
+	ast_pthread_mutex_unlock(&userl.lock);
+	return res;
+}
+
+static int raw_hangup(struct sockaddr_in *sin, short src, short dst)
+{
+	struct ast_iax2_full_hdr fh;
+	fh.scallno = htons(src | AST_FLAG_FULL);
+	fh.dcallno = htons(dst);
+	fh.ts = 0;
+	fh.oseqno = 0;
+	fh.type = AST_FRAME_IAX;
+	fh.csub = compress_subclass(AST_IAX2_COMMAND_INVAL);
+#if 0
+	if (option_debug)
+#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_iax2_pvt *p)
+{
+	struct iax_ie_data ied;
+	memset(&ied, 0, sizeof(ied));
+	iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods);
+	if (p->authmethods & (IAX_AUTH_MD5 | IAX_AUTH_RSA)) {
+		snprintf(p->challenge, sizeof(p->challenge), "%d", rand());
+		iax_ie_append_str(&ied, IAX_IE_CHALLENGE, p->challenge);
+	}
+	iax_ie_append_str(&ied,IAX_IE_USERNAME, p->username);
+	return send_command(p, AST_FRAME_IAX, AST_IAX2_COMMAND_AUTHREQ, 0, ied.buf, ied.pos, -1);
+}
+
+static int authenticate_verify(struct chan_iax2_pvt *p, struct iax_ies *ies)
+{
+	char requeststr[256] = "";
+	char md5secret[256] = "";
+	char secret[256] = "";
+	char rsasecret[256] = "";
+	int res = -1; 
+	int x;
+	
+	if (!(p->state & IAX_STATE_AUTHENTICATED))
+		return res;
+	if (ies->password)
+		strncpy(secret, ies->password, sizeof(secret) - 1);
+	if (ies->md5_result)
+		strncpy(md5secret, ies->md5_result, sizeof(md5secret)-1);
+	if (ies->rsa_result)
+		strncpy(rsasecret, ies->rsa_result, sizeof(rsasecret)-1);
+	if ((p->authmethods & IAX_AUTH_RSA) && strlen(rsasecret) && strlen(p->inkeys)) {
+		struct ast_key *key;
+		char *keyn;
+		char tmpkey[256];
+		char *stringp=NULL;
+		strncpy(tmpkey, p->inkeys, sizeof(tmpkey));
+		stringp=tmpkey;
+		keyn = strsep(&stringp, ":");
+		while(keyn) {
+			key = ast_key_get(keyn, AST_KEY_PUBLIC);
+			if (key && !ast_check_signature(key, p->challenge, rsasecret)) {
+				res = 0;
+				break;
+			} else if (!key)
+				ast_log(LOG_WARNING, "requested inkey '%s' for RSA authentication does not exist\n", keyn);
+			keyn = strsep(&stringp, ":");
+		}
+	} else if (p->authmethods & IAX_AUTH_MD5) {
+		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);
+		/* If they support md5, authenticate with it.  */
+		for (x=0;x<16;x++)
+			sprintf(requeststr + (x << 1), "%2.2x", digest[x]);
+		if (!strcasecmp(requeststr, md5secret))
+			res = 0;
+	} else if (p->authmethods & IAX_AUTH_PLAINTEXT) {
+		if (!strcmp(secret, p->secret))
+			res = 0;
+	}
+	return res;
+}
+
+static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *ies)
+{
+	char requeststr[256] = "";
+	char peer[256] = "";
+	char md5secret[256] = "";
+	char rsasecret[256] = "";
+	char secret[256] = "";
+	struct iax2_peer *p;
+	struct ast_key *key;
+	char *keyn;
+	int x;
+	int expire = 0;
+
+	iaxs[callno]->state &= ~IAX_STATE_AUTHENTICATED;
+	strcpy(iaxs[callno]->peer, "");
+	if (ies->username)
+		strncpy(peer, ies->username, sizeof(peer) - 1);
+	if (ies->password)
+		strncpy(secret, ies->password, sizeof(secret) - 1);
+	if (ies->md5_result)
+		strncpy(md5secret, ies->md5_result, sizeof(md5secret)-1);
+	if (ies->rsa_result)
+		strncpy(rsasecret, ies->rsa_result, sizeof(rsasecret)-1);
+	if (ies->refresh)
+		expire = ies->refresh;
+
+	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) && (p->authmethods & IAX_AUTH_RSA) && strlen(p->challenge)) {
+		if (strlen(p->inkeys)) {
+			char tmpkeys[256];
+			char *stringp=NULL;
+			strncpy(tmpkeys, p->inkeys, sizeof(tmpkeys));
+			stringp=tmpkeys;
+			keyn = strsep(&stringp, ":");
+			while(keyn) {
+				key = ast_key_get(keyn, AST_KEY_PUBLIC);
+				if (key && !ast_check_signature(key, p->challenge, rsasecret)) {
+					iaxs[callno]->state |= IAX_STATE_AUTHENTICATED;
+					break;
+				} else if (!key) 
+					ast_log(LOG_WARNING, "requested inkey '%s' does not exist\n", keyn);
+				keyn = strsep(&stringp, ":");
+			}
+			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) && (p->authmethods & IAX_AUTH_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) && (p->authmethods & IAX_AUTH_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++)
+			sprintf(requeststr + (x << 1), "%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, int authmethods, struct iax_ie_data *ied, struct sockaddr_in *sin)
+{
+	int res = -1;
+	int x;
+	if (keyn && strlen(keyn)) {
+		if (!(authmethods & IAX_AUTH_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 {
+					iax_ie_append_str(ied, IAX_IE_RSA_RESULT, sig);
+					res = 0;
+				}
+			}
+		}
+	} 
+	/* Fall back */
+	if (res && secret && strlen(secret)) {
+		if ((authmethods & IAX_AUTH_MD5) && strlen(challenge)) {
+			struct MD5Context md5;
+			unsigned char digest[16];
+			char digres[128] = "";
+			MD5Init(&md5);
+			MD5Update(&md5, challenge, strlen(challenge));
+			MD5Update(&md5, secret, strlen(secret));
+			MD5Final(digest, &md5);
+			/* If they support md5, authenticate with it.  */
+			for (x=0;x<16;x++)
+				sprintf(digres + (x << 1),  "%2.2x", digest[x]);
+			iax_ie_append_str(ied, IAX_IE_MD5_RESULT, digres);
+			res = 0;
+		} else if (authmethods & IAX_AUTH_PLAINTEXT) {
+			iax_ie_append_str(ied, IAX_IE_PASSWORD, secret);
+			res = 0;
+		} else
+			ast_log(LOG_NOTICE, "No way to send secret to peer '%s' (their methods: %d)\n", inet_ntoa(sin->sin_addr), authmethods);
+	}
+	return res;
+}
+
+static int authenticate_reply(struct chan_iax2_pvt *p, struct sockaddr_in *sin, struct iax_ies *ies, char *override, char *okey)
+{
+	struct iax2_peer *peer;
+	/* Start pessimistic */
+	int res = -1;
+	int authmethods = 0;
+	struct iax_ie_data ied;
+	
+	memset(&ied, 0, sizeof(ied));
+	
+	if (ies->username)
+		strncpy(p->username, ies->username, sizeof(p->username) - 1);
+	if (ies->challenge)
+		strncpy(p->challenge, ies->challenge, sizeof(p->challenge)-1);
+	if (ies->authmethods)
+		authmethods = ies->authmethods;
+
+	/* Check for override RSA authentication first */
+	if ((override && strlen(override)) || (okey && strlen(okey))) {
+		/* Normal password authentication */
+		res = authenticate(p->challenge, override, okey, authmethods, &ied, 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 */
+			) {
+				res = authenticate(p->challenge, peer->secret, peer->outkey, authmethods, &ied, sin);
+				if (!res)
+					break;	
+			}
+			peer = peer->next;
+		}
+		ast_pthread_mutex_unlock(&peerl.lock);
+	}
+	if (!res)
+		res = send_command(p, AST_FRAME_IAX, AST_IAX2_COMMAND_AUTHREP, 0, ied.buf, ied.pos, -1);
+	return res;
+}
+
+static int iax2_do_register(struct iax2_registry *reg);
+
+static int iax2_do_register_s(void *data)
+{
+	struct iax2_registry *reg = data;
+	reg->expire = -1;
+	iax2_do_register(reg);
+	return 0;
+}
+
+static int try_transfer(struct chan_iax2_pvt *pvt, struct iax_ies *ies)
+{
+	int newcall = 0;
+	char newip[256] = "";
+	
+	struct sockaddr_in new;
+	
+	if (ies->apparent_addr)
+		memcpy(&new, ies->apparent_addr, sizeof(new));
+	if (ies->callno)
+		newcall = ies->callno;
+	if (!newcall || !new.sin_addr.s_addr || !new.sin_port) {
+		ast_log(LOG_WARNING, "Invalid transfer request\n");
+		return -1;
+	}
+	pvt->transfercallno = newcall;
+	memcpy(&pvt->transfer, &new, sizeof(pvt->transfer));
+	inet_aton(newip, &pvt->transfer.sin_addr);
+	pvt->transfer.sin_family = AF_INET;
+	pvt->transferring = TRANSFER_BEGIN;
+	send_command_transfer(pvt, AST_FRAME_IAX, AST_IAX2_COMMAND_TXCNT, 0, NULL, 0);
+	return 0; 
+}
+
+static int complete_dpreply(struct chan_iax2_pvt *pvt, struct iax_ies *ies)
+{
+	char exten[256] = "";
+	int status = CACHE_FLAG_UNKNOWN;
+	int expirey = iaxdefaultdpcache;
+	int x;
+	int matchmore = 0;
+	struct iax2_dpcache *dp, *prev;
+	
+	if (ies->called_number)
+		strncpy(exten, ies->called_number, sizeof(exten)-1);
+
+	if (ies->dpstatus & IAX_DPSTATUS_EXISTS)
+		status = CACHE_FLAG_EXISTS;
+	else if (ies->dpstatus & IAX_DPSTATUS_CANEXIST)
+		status = CACHE_FLAG_CANEXIST;
+	else if (ies->dpstatus & IAX_DPSTATUS_NONEXISTANT)
+		status = CACHE_FLAG_NONEXISTANT;
+
+	if (ies->dpstatus & IAX_DPSTATUS_IGNOREPAT) {
+		/* Don't really do anything with this */
+	}
+	if (ies->refresh)
+		expirey = ies->refresh;
+	if (ies->dpstatus & IAX_DPSTATUS_MATCHMORE)
+		matchmore = CACHE_FLAG_MATCHMORE;
+	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 = 0;
+			dp->expirey.tv_sec = dp->orig.tv_sec + expirey;
+			if (dp->flags & CACHE_FLAG_PENDING) {
+				dp->flags &= ~CACHE_FLAG_PENDING;
+				dp->flags |= status;
+				dp->flags |= CACHE_FLAG_MATCHMORE;
+			}
+			/* Wake up waiters */
+			for (x=0;x<sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++)
+				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, struct iax_ies *ies)
+{
+	int peercallno = 0;
+	struct chan_iax2_pvt *pvt = iaxs[callno];
+	struct ast_iax2_frame *cur;
+
+	if (ies->callno)
+		peercallno = ies->callno;
+
+	if (peercallno < 1) {
+		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 iax2_ack_registry(struct iax_ies *ies, struct sockaddr_in *sin, int callno)
+{
+	struct iax2_registry *reg;
+	/* Start pessimistic */
+	char peer[256] = "";
+	int refresh = 0;
+	char ourip[256] = "<Unspecified>";
+	struct sockaddr_in oldus;
+	struct sockaddr_in us;
+
+	memset(&us, 0, sizeof(us));
+	if (ies->apparent_addr)
+		memcpy(&us, ies->apparent_addr, sizeof(us));
+	if (ies->username)
+		strncpy(peer, ies->username, sizeof(peer) - 1);
+	if (ies->refresh)
+		refresh = ies->refresh;
+	if (ies->calling_number) {
+		/* We don't do anything with it really, but maybe we should */
+	}
+	reg = iaxs[callno]->reg;
+	if (!reg) {
+		ast_log(LOG_WARNING, "Registry acknowledge on unknown registery '%s'\n", peer);
+		return -1;
+	}
+	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;
+	}
+	memcpy(&reg->us, &us, sizeof(reg->us));
+	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, iax2_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;
+}
+
+static int iax2_register(char *value, int lineno)
+{
+	struct iax2_registry *reg;
+	char copy[256];
+	char *username, *hostname, *secret;
+	char *porta;
+	char *stringp=NULL;
+	
+	struct hostent *hp;
+	if (!value)
+		return -1;
+	strncpy(copy, value, sizeof(copy)-1);
+	stringp=copy;
+	username = strsep(&stringp, "@");
+	hostname = strsep(&stringp, "@");
+	if (!hostname) {
+		ast_log(LOG_WARNING, "Format for registration is user[:secret]@host[:port] at line %d", lineno);
+		return -1;
+	}
+	stringp=username;
+	username = strsep(&stringp, ":");
+	secret = strsep(&stringp, ":");
+	stringp=hostname;
+	hostname = strsep(&stringp, ":");
+	porta = strsep(&stringp, ":");
+	
+	if (porta && !atoi(porta)) {
+		ast_log(LOG_WARNING, "%s is not a valid port number at line %d\n", porta, lineno);
+		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 iax2_registry));
+	if (reg) {
+		memset(reg, 0, sizeof(struct iax2_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 = 0;
+		registrations = reg;
+	} else {
+		ast_log(LOG_ERROR, "Out of memory\n");
+		return -1;
+	}
+	return 0;
+}
+
+static int expire_registry(void *data)
+{
+	struct iax2_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 iax2_poke_peer(struct iax2_peer *peer);
+
+
+static int update_registry(char *name, struct sockaddr_in *sin, int callno)
+{
+	/* Called from IAX thread only, with proper iaxsl lock */
+	struct iax_ie_data ied;
+	struct iax2_peer *p;
+	memset(&ied, 0, sizeof(ied));
+	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));
+				iax2_poke_peer(p);
+			}		
+			/* 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);
+			iax_ie_append_str(&ied, IAX_IE_USERNAME, p->name);
+			iax_ie_append_short(&ied, IAX_IE_REFRESH, p->expirey);
+			iax_ie_append_addr(&ied, IAX_IE_APPARENT_ADDR, &p->addr);
+			if (p->hascallerid)
+				iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, p->callerid);
+			return send_command_final(iaxs[callno], AST_FRAME_IAX, AST_IAX2_COMMAND_REGACK, 0, ied.buf, ied.pos, -1);;
+		}
+	}
+	ast_log(LOG_WARNING, "No such peer '%s'\n", name);
+	return -1;
+}
+
+static int registry_authrequest(char *name, int callno)
+{
+	struct iax_ie_data ied;
+	struct iax2_peer *p;
+	for (p = peerl.peers;p;p = p->next) {
+		if (!strcasecmp(name, p->name)) {
+			memset(&ied, 0, sizeof(ied));
+			iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods);
+			if (p->authmethods & (IAX_AUTH_RSA | IAX_AUTH_MD5)) {
+				/* Build the challenge */
+				snprintf(p->challenge, sizeof(p->challenge), "%d", rand());
+				iax_ie_append_str(&ied, IAX_IE_CHALLENGE, p->challenge);
+			}
+			iax_ie_append_str(&ied, IAX_IE_USERNAME, name);
+			return send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX2_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1);;
+		}
+	}
+	ast_log(LOG_WARNING, "No such peer '%s'\n", name);
+	return 0;
+}
+
+static int registry_rerequest(struct iax_ies *ies, int callno, struct sockaddr_in *sin)
+{
+	struct iax2_registry *reg;
+	/* Start pessimistic */
+	struct iax_ie_data ied;
+	char peer[256] = "";
+	char challenge[256] = "";
+	int res;
+	int authmethods = 0;
+	if (ies->authmethods)
+		authmethods = ies->authmethods;
+	if (ies->username)
+		strncpy(peer, ies->username, sizeof(peer) - 1);
+	if (ies->challenge)
+		strncpy(challenge, ies->challenge, sizeof(challenge) - 1);
+	memset(&ied, 0, sizeof(ied));
+	reg = iaxs[callno]->reg;
+			if (memcmp(&reg->addr, sin, sizeof(&reg->addr))) {
+				ast_log(LOG_WARNING, "Received unsolicited registry authenticate request from '%s'\n", inet_ntoa(sin->sin_addr));
+				return -1;
+			}
+			if (!strlen(reg->secret)) {
+				ast_log(LOG_NOTICE, "No secret associated with peer '%s'\n", reg->username);
+				reg->regstate = REG_STATE_NOAUTH;
+				return -1;
+			}
+			iax_ie_append_str(&ied, IAX_IE_USERNAME, reg->username);
+			iax_ie_append_short(&ied, IAX_IE_REFRESH, 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, authmethods, &ied, sin);
+			} else
+				res = authenticate(challenge, reg->secret, NULL, authmethods, &ied, sin);
+			if (!res) {
+				reg->regstate = REG_STATE_AUTHSENT;
+				return send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX2_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1);
+			} else
+				return -1;
+	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;
+		if (iaxs[callno]->initid > -1)
+			ast_sched_del(sched, iaxs[callno]->initid);
+		iaxs[callno]->initid = -1;
+		return 0;
+}
+
+static int auto_hangup(void *nothing)
+{
+	/* Called from IAX thread only, without iaxs lock */
+	int callno = (int)(long)(nothing);
+	struct iax_ie_data ied;
+	ast_pthread_mutex_lock(&iaxsl[callno]);
+	if (iaxs[callno]) {
+		iaxs[callno]->autoid = -1;
+		memset(&ied, 0, sizeof(ied));
+		iax_ie_append_str(&ied, IAX_IE_CAUSE, "Timeout");
+		send_command_final(iaxs[callno], AST_FRAME_IAX, AST_IAX2_COMMAND_HANGUP, 0, ied.buf, ied.pos, -1);
+	}
+	ast_pthread_mutex_unlock(&iaxsl[callno]);
+	return 0;
+}
+
+static void iax2_dprequest(struct iax2_dpcache *dp, int callno)
+{
+	struct iax_ie_data ied;
+	/* 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);
+	memset(&ied, 0, sizeof(ied));
+	iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, dp->exten);
+	send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX2_COMMAND_DPREQ, 0, ied.buf, ied.pos, -1);
+	dp->flags |= CACHE_FLAG_TRANSMITTED;
+}
+
+static int iax2_vnak(int callno)
+{
+	return send_command_immediate(iaxs[callno], AST_FRAME_IAX, AST_IAX2_COMMAND_VNAK, 0, NULL, 0, iaxs[callno]->iseqno);
+}
+
+static void vnak_retransmit(int callno, int last)
+{
+	struct ast_iax2_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->oseqno >= last)) {
+			send_packet(f);
+		}
+		f = f->next;
+	}
+	ast_pthread_mutex_unlock(&iaxq.lock);
+}
+
+static int iax2_poke_peer_s(void *data)
+{
+	struct iax2_peer *peer = data;
+	peer->pokeexpire = -1;
+	iax2_poke_peer(peer);
+	return 0;
+}
+
+static int parse_ies(struct iax_ies *ies, unsigned char *data, int datalen)
+{
+	/* Parse data into information elements */
+	int len;
+	int ie;
+	memset(ies, 0, sizeof(struct iax_ies));
+	while(datalen >= 2) {
+		ie = data[0];
+		len = data[1];
+		if (len > datalen - 2) {
+			ast_log(LOG_WARNING, "Information element length exceeds message size\n");
+			return -1;
+		}
+		ast_log(LOG_DEBUG, "IE '%s' (%d) of length %d\n", ie2str(ie), ie, len);
+		switch(ie) {
+		case IAX_IE_CALLED_NUMBER:
+			ies->called_number = data + 2;
+			break;
+		case IAX_IE_CALLING_NUMBER:
+			ies->calling_number = data + 2;
+			break;
+		case IAX_IE_CALLING_ANI:
+			ies->calling_ani = data + 2;
+			break;
+		case IAX_IE_CALLING_NAME:
+			ies->calling_name = data + 2;
+			break;
+		case IAX_IE_CALLED_CONTEXT:
+			ies->called_context = data + 2;
+			break;
+		case IAX_IE_USERNAME:
+			ies->username = data + 2;
+			break;
+		case IAX_IE_PASSWORD:
+			ies->password = data + 2;
+			break;
+		case IAX_IE_CAPABILITY:
+			if (len != sizeof(unsigned int)) 
+				ast_log(LOG_WARNING, "Expecting capability to be %d bytes long but was %d\n", sizeof(unsigned int), len);
+			else
+				ies->capability = ntohl(*((unsigned int *)(data + 2)));
+			break;
+		case IAX_IE_FORMAT:
+			if (len != sizeof(unsigned int)) 
+				ast_log(LOG_WARNING, "Expecting format to be %d bytes long but was %d\n", sizeof(unsigned int), len);
+			else
+				ies->format = ntohl(*((unsigned int *)(data + 2)));
+			break;
+		case IAX_IE_LANGUAGE:
+			ies->language = data + 2;
+			break;
+		case IAX_IE_VERSION:
+			if (len != sizeof(unsigned short)) 
+				ast_log(LOG_WARNING, "Expecting version to be %d bytes long but was %d\n", sizeof(unsigned short), len);
+			else
+				ies->version = ntohs(*((unsigned short *)(data + 2)));
+			break;
+		case IAX_IE_ADSICPE:
+			if (len != sizeof(unsigned int)) 
+				ast_log(LOG_WARNING, "Expecting adsicpe to be %d bytes long but was %d\n", sizeof(unsigned int), len);
+			else
+				ies->adsicpe = ntohl(*((unsigned int *)(data + 2)));
+			break;
+		case IAX_IE_DNID:
+			ies->dnid = data + 2;
+			break;
+		case IAX_IE_AUTHMETHODS:
+			if (len != sizeof(unsigned short)) 
+				ast_log(LOG_WARNING, "Expecting authmethods to be %d bytes long but was %d\n", sizeof(unsigned short), len);
+			else
+				ies->authmethods = ntohs(*((unsigned short *)(data + 2)));
+			break;
+		case IAX_IE_CHALLENGE:
+			ies->challenge = data + 2;
+			break;
+		case IAX_IE_MD5_RESULT:
+			ies->md5_result = data + 2;
+			break;
+		case IAX_IE_RSA_RESULT:
+			ies->md5_result = data + 2;
+			break;
+		case IAX_IE_APPARENT_ADDR:
+			ies->apparent_addr = ((struct sockaddr_in *)(data + 2));
+			break;
+		case IAX_IE_REFRESH:
+			if (len != sizeof(unsigned short)) 
+				ast_log(LOG_WARNING, "Expecting refresh to be %d bytes long but was %d\n", sizeof(unsigned short), len);
+			else
+				ies->refresh = ntohs(*((unsigned short *)(data + 2)));
+			break;
+		case IAX_IE_DPSTATUS:
+			if (len != sizeof(unsigned short)) 
+				ast_log(LOG_WARNING, "Expecting dpstatus to be %d bytes long but was %d\n", sizeof(unsigned short), len);
+			else
+				ies->dpstatus = ntohs(*((unsigned short *)(data + 2)));
+			break;
+		case IAX_IE_CALLNO:
+			if (len != sizeof(unsigned short)) 
+				ast_log(LOG_WARNING, "Expecting callno to be %d bytes long but was %d\n", sizeof(unsigned short), len);
+			else
+				ies->callno = ntohs(*((unsigned short *)(data + 2)));
+			break;
+		case IAX_IE_CAUSE:
+			ies->cause = data + 2;
+			break;
+		default:
+			ast_log(LOG_NOTICE, "Ignoring unknown information element '%s' (%d) of length %d\n", ie2str(ie), ie, len);
+		}
+		/* Overwrite information element with 0, to null terminate previous portion */
+		data[0] = 0;
+		datalen -= (len + 2);
+		data += (len + 2);
+	}
+	if (datalen) {
+		ast_log(LOG_WARNING, "Invalid information element contents, strange boundary\n");
+		return -1;
+	}
+	return 0;
+}
+
+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 = 0;
+	struct ast_iax2_full_hdr *fh = (struct ast_iax2_full_hdr *)buf;
+	struct ast_iax2_mini_hdr *mh = (struct ast_iax2_mini_hdr *)buf;
+	struct ast_iax2_frame fr, *cur;
+	struct ast_frame f;
+	struct ast_channel *c;
+	struct iax2_dpcache *dp;
+	struct iax2_peer *peer;
+	struct iax_ies ies;
+	struct iax_ie_data ied0, ied1;
+	int format;
+	int exists;
+	int mm;
+	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_iax2_mini_hdr)) {
+		ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, sizeof(struct ast_iax2_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);
+		/* Retrieve the type and subclass */
+		f.frametype = fh->type;
+		f.subclass = uncompress_subclass(fh->csub);
+#if 0
+		f.subclass = fh->subclasshigh << 16;
+		f.subclass += ntohs(fh->subclasslow);
+#endif
+		if ((f.frametype == AST_FRAME_IAX) && ((f.subclass == AST_IAX2_COMMAND_NEW) || (f.subclass == AST_IAX2_COMMAND_REGREQ)
+				|| (f.subclass == AST_IAX2_COMMAND_POKE)))
+			new = NEW_ALLOW;
+	} else {
+		/* Don't knwo anything about it yet */
+		f.frametype = AST_FRAME_NULL;
+		f.subclass = 0;
+	}
+
+	fr.callno = find_callno(ntohs(mh->callno) & ~AST_FLAG_FULL, dcallno, &sin, new);
+
+	if (fr.callno > 0) 
+		ast_pthread_mutex_lock(&iaxsl[fr.callno]);
+
+	if (!fr.callno || !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_IAX2_COMMAND_INVAL) &&
+				 (f.subclass != AST_IAX2_COMMAND_TXCNT) &&
+				 (f.subclass != AST_IAX2_COMMAND_TXACC))||
+			    (f.frametype != AST_FRAME_IAX))
+				raw_hangup(&sin, ntohs(fh->dcallno), ntohs(mh->callno) & ~AST_FLAG_FULL
+				);
+		}
+		if (fr.callno > 0) 
+			ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
+		return 1;
+	}
+	if (((f.subclass != AST_IAX2_COMMAND_TXCNT) &&
+	     (f.subclass != AST_IAX2_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->oseqno), f.frametype, f.subclass);
+		/* Check if it's out of order (and not an ACK or INVAL) */
+		fr.oseqno = ntohs(fh->oseqno);
+		fr.iseqno = ntohs(fh->iseqno);
+		if ((iaxs[fr.callno]->iseqno != fr.oseqno) &&
+			(iaxs[fr.callno]->iseqno ||
+				((f.subclass != AST_IAX2_COMMAND_TXCNT) &&
+				(f.subclass != AST_IAX2_COMMAND_TXACC)) ||
+				(f.subclass != AST_FRAME_IAX))) {
+			if (
+			 ((f.subclass != AST_IAX2_COMMAND_ACK) &&
+			  (f.subclass != AST_IAX2_COMMAND_INVAL) &&
+			  (f.subclass != AST_IAX2_COMMAND_TXCNT) &&
+			  (f.subclass != AST_IAX2_COMMAND_TXACC) &&
+			  (f.subclass != AST_IAX2_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", 
+					iaxs[fr.callno]->iseqno, fr.oseqno, f.frametype, f.subclass);
+				if (iaxs[fr.callno]->iseqno > fr.oseqno) {
+					/* If we've already seen it, ack it XXX There's a border condition here XXX */
+					if ((f.frametype != AST_FRAME_IAX) || 
+							((f.subclass != AST_IAX2_COMMAND_ACK) && (f.subclass != AST_IAX2_COMMAND_INVAL))) {
+						if (option_debug)
+							ast_log(LOG_DEBUG, "Acking anyway\n");
+						/* XXX Maybe we should handle its ack to us, but then again, it's probably outdated anyway, and if
+						   we have anything to send, we'll retransmit and get an ACK back anyway XXX */
+						send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
+					}
+				} else {
+					/* Send a VNAK requesting retransmission */
+					iax2_vnak(fr.callno);
+				}
+				ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
+				return 1;
+			}
+		} else {
+			/* Increment unless it's an ACK or VNAK */
+			if (((f.subclass != AST_IAX2_COMMAND_ACK) &&
+			    (f.subclass != AST_IAX2_COMMAND_INVAL) &&
+			    (f.subclass != AST_IAX2_COMMAND_TXCNT) &&
+			    (f.subclass != AST_IAX2_COMMAND_TXACC) &&
+				(f.subclass != AST_IAX2_COMMAND_VNAK)) ||
+			    (f.frametype != AST_FRAME_IAX))
+				iaxs[fr.callno]->iseqno++;
+		}
+		/* Handle implicit ACKing unless this is an INVAL */
+		if (((f.subclass != AST_IAX2_COMMAND_INVAL)) ||
+			(f.frametype != AST_FRAME_IAX)) {
+			int x;
+			/* XXX This code is not very efficient.  Surely there is a better way which still
+			       properly handles boundary conditions? XXX */
+			/* First we have to qualify that the ACKed value is within our window */
+			for (x=iaxs[fr.callno]->rseqno; x != iaxs[fr.callno]->oseqno; x++)
+				if (fr.iseqno == x)
+					break;
+			if ((x != iaxs[fr.callno]->oseqno) || (iaxs[fr.callno]->oseqno == fr.iseqno)) {
+				/* The acknowledgement is within our window.  Time to acknowledge everything
+				   that it says to */
+				for (x=iaxs[fr.callno]->rseqno; x != fr.iseqno; x++) {
+					/* Ack the packet with the given timestamp */
+					if (option_debug)
+						ast_log(LOG_DEBUG, "Cancelling transmission of packet %d\n", x);
+					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) && (x == cur->oseqno)) {
+							cur->retries = -1;
+							/* Destroy call if this is the end */
+							if (cur->final) { 
+								if (option_debug)
+									ast_log(LOG_DEBUG, "Really destroying %d, having been acked on final message\n", fr.callno);
+								iax2_destroy_nolock(fr.callno);
+							}
+						}
+					}
+					ast_pthread_mutex_unlock(&iaxq.lock);
+				}
+				/* Note how much we've received acknowledgement for */
+				iaxs[fr.callno]->rseqno = fr.iseqno;
+			} else
+				ast_log(LOG_DEBUG, "Received iseqno %d not within window %d->%d\n", fr.iseqno, iaxs[fr.callno]->rseqno, iaxs[fr.callno]->oseqno);
+		}
+		/* A full frame */
+		if (res < sizeof(struct ast_iax2_full_hdr)) {
+			ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, sizeof(struct ast_iax2_full_hdr));
+			ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
+			return 1;
+		}
+		f.datalen = res - sizeof(struct ast_iax2_full_hdr);
+		if (f.datalen) {
+			if (f.frametype == AST_FRAME_IAX) {
+				if (parse_ies(&ies, buf + sizeof(struct ast_iax2_full_hdr), f.datalen)) {
+					ast_log(LOG_WARNING, "undecodable frame received\n");
+					ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
+					return 1;
+				}
+				f.data = NULL;
+			} else
+				f.data = buf + sizeof(struct ast_iax2_full_hdr);
+		} else {
+			if (f.frametype == AST_FRAME_IAX)
+				f.data = NULL;
+			else
+				f.data = empty;
+			memset(&ies, 0, sizeof(ies));
+		}
+		fr.ts = ntohl(fh->ts);
+		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) {
+			if (iaxs[fr.callno]->initid > -1) {
+				/* Don't auto congest anymore since we've gotten something usefulb ack */
+				ast_sched_del(sched, iaxs[fr.callno]->initid);
+				iaxs[fr.callno]->initid = -1;
+			}
+			/* Handle the IAX pseudo frame itself */
+			if (option_debug)
+				ast_log(LOG_DEBUG, "IAX subclass %d received\n", f.subclass);
+			/* 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_IAX2_COMMAND_LAGRQ)
+				schedule_delivery(&fr, 0);
+			switch(f.subclass) {
+			case AST_IAX2_COMMAND_ACK:
+				/* Do nothing */
+				break;
+			case AST_IAX2_COMMAND_QUELCH:
+				if (iaxs[fr.callno]->state & IAX_STATE_STARTED)
+					iaxs[fr.callno]->quelch = 1;
+				break;
+			case AST_IAX2_COMMAND_UNQUELCH:
+				if (iaxs[fr.callno]->state & IAX_STATE_STARTED)
+					iaxs[fr.callno]->quelch = 0;
+				break;
+			case AST_IAX2_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);
+					memset(&ied1, 0, sizeof(ied1));
+					iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr.callno]->callno);
+					send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_TXREADY, 0, ied1.buf, ied1.pos, -1);
+					iaxs[fr.callno]->transferring = TRANSFER_READY;
+				}
+				break;
+			case AST_IAX2_COMMAND_NEW:
+				/* Ignore if it's already up */
+				if (iaxs[fr.callno]->state & (IAX_STATE_STARTED | IAX_STATE_TBD))
+					break;
+				if (check_access(fr.callno, &sin, &ies)) {
+					/* They're not allowed on */
+					memset(&ied0, 0, sizeof(ied0));
+					iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No authority found");
+					send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+					ast_log(LOG_NOTICE, "Rejected connect attempt from %s\n", inet_ntoa(sin.sin_addr));
+					break;
+				}
+				/* 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);
+				if (!strlen(iaxs[fr.callno]->secret) && !strlen(iaxs[fr.callno]->inkeys)) {
+					if (strcmp(iaxs[fr.callno]->exten, "TBD") && !exists) {
+						memset(&ied0, 0, sizeof(ied0));
+						iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
+						send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -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 & iax2_capability;
+						if (!format) {
+							format = iaxs[fr.callno]->peercapability & iax2_capability;
+							if (!format) {
+								memset(&ied0, 0, sizeof(ied0));
+								iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
+								send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -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, iax2_capability);
+							} else {
+								/* Pick one... */
+								format = ast_best_codec(iaxs[fr.callno]->peercapability & iax2_capability);
+								if (!format) {
+									memset(&ied0, 0, sizeof(ied0));
+									iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
+									ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[fr.callno]->peercapability & iax2_capability);
+									send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -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, iax2_capability);
+									iaxs[fr.callno]->alreadygone = 1;
+									break;
+								}
+							}
+						}
+						if (format) {
+							/* No authentication required, let them in */
+							memset(&ied1, 0, sizeof(ied1));
+							iax_ie_append_int(&ied1, IAX_IE_FORMAT, format);
+							send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -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_iax2_new(iaxs[fr.callno], AST_STATE_RING, format)))
+									iax2_destroy_nolock(fr.callno);
+							} else {
+								iaxs[fr.callno]->state |= IAX_STATE_TBD;
+								/* If this is a TBD call, we're ready but now what...  */
+								if (option_verbose > 2)
+									ast_verbose(VERBOSE_PREFIX_3 "Accepted unauthenticated TBD call from %s\n", inet_ntoa(sin.sin_addr));
+							}
+						}
+					}
+					break;
+				}
+				authenticate_request(iaxs[fr.callno]);
+				iaxs[fr.callno]->state |= IAX_STATE_AUTHENTICATED;
+				break;
+			case AST_IAX2_COMMAND_DPREQ:
+				/* Request status in the dialplan */
+				if ((iaxs[fr.callno]->state & IAX_STATE_TBD) && 
+					!(iaxs[fr.callno]->state & IAX_STATE_STARTED) && ies.called_number) {
+					unsigned short dpstatus = 0;
+					memset(&ied1, 0, sizeof(ied1));
+					mm = ast_matchmore_extension(NULL, iaxs[fr.callno]->context, ies.called_number, 1, iaxs[fr.callno]->callerid);
+					/* Must be started */
+					if (ast_exists_extension(NULL, iaxs[fr.callno]->context, ies.called_number, 1, iaxs[fr.callno]->callerid)) {
+						dpstatus = IAX_DPSTATUS_EXISTS;
+					} else if (ast_canmatch_extension(NULL, iaxs[fr.callno]->context, ies.called_number, 1, iaxs[fr.callno]->callerid)) {
+						dpstatus = IAX_DPSTATUS_CANEXIST;
+					} else {
+						dpstatus = IAX_DPSTATUS_NONEXISTANT;
+					}
+					if (ast_ignore_pattern(iaxs[fr.callno]->context, ies.called_number))
+						dpstatus |= IAX_DPSTATUS_IGNOREPAT;
+					if (mm)
+						dpstatus |= IAX_DPSTATUS_MATCHMORE;
+					iax_ie_append_str(&ied1, IAX_IE_CALLED_NUMBER, ies.called_number);
+					iax_ie_append_short(&ied1, IAX_IE_DPSTATUS, dpstatus);
+					iax_ie_append_short(&ied1, IAX_IE_REFRESH, iaxdefaultdpcache);
+					send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_DPREP, 0, ied1.buf, ied1.pos, -1);
+				}
+				break;
+			case AST_IAX2_COMMAND_HANGUP:
+				iaxs[fr.callno]->alreadygone = 1;
+				ast_log(LOG_DEBUG, "Immediately destroying %d, having received hangup\n", fr.callno);
+				iax2_destroy_nolock(fr.callno);
+				break;
+			case AST_IAX2_COMMAND_REJECT:
+				if (iaxs[fr.callno]->owner)
+					ast_log(LOG_WARNING, "Call rejected by %s: %s\n", inet_ntoa(iaxs[fr.callno]->addr.sin_addr), ies.cause ? ies.cause : "<Unknown>");
+				iaxs[fr.callno]->error = EPERM;
+				ast_log(LOG_DEBUG, "Immediately destroying %d, having received reject\n", fr.callno);
+				iax2_destroy_nolock(fr.callno);
+				break;
+			case AST_IAX2_COMMAND_ACCEPT:
+				/* 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 (ies.format) {
+					iaxs[fr.callno]->peerformat = ies.format;
+				} else {
+					if (iaxs[fr.callno]->owner)
+						iaxs[fr.callno]->peerformat = iaxs[fr.callno]->owner->nativeformats;
+					else
+						iaxs[fr.callno]->peerformat = iax2_capability;
+				}
+				if (option_verbose > 2)
+					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)) {
+					memset(&ied0, 0, sizeof(ied0));
+					iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
+					send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -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->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)) {
+						iax2_dprequest(dp, fr.callno);
+					}
+					dp = dp->peer;
+				}
+				ast_pthread_mutex_unlock(&dpcache_lock);
+				break;
+			case AST_IAX2_COMMAND_POKE:
+				/* Send back a pong packet with the original timestamp */
+				send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_PONG, fr.ts, NULL, 0, -1);
+				break;
+			case AST_IAX2_COMMAND_PING:
+#ifdef BRIDGE_OPTIMIZATION
+				if (iaxs[fr.callno]->bridgecallno) {
+					/* If we're in a bridged call, just forward this */
+					forward_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_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_IAX2_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_IAX2_COMMAND_PONG, fr.ts, NULL, 0, -1);
+#endif			
+				break;
+			case AST_IAX2_COMMAND_PONG:
+#ifdef BRIDGE_OPTIMIZATION
+				if (iaxs[fr.callno]->bridgecallno) {
+					/* Forward to the other side of the bridge */
+					forward_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_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
+				if (iaxs[fr.callno]->peerpoke) {
+					peer = iaxs[fr.callno]->peerpoke;
+					if ((peer->lastms < 0)  || (peer->lastms > peer->maxms)) {
+						if (iaxs[fr.callno]->pingtime <= peer->maxms)
+							ast_log(LOG_NOTICE, "Peer '%s' is now REACHABLE!\n", peer->name);
+					} else if ((peer->lastms > 0) && (peer->lastms <= peer->maxms)) {
+						if (iaxs[fr.callno]->pingtime > peer->maxms)
+							ast_log(LOG_NOTICE, "Peer '%s' is now TOO LAGGED (%d ms)!\n", peer->name, iaxs[fr.callno]->pingtime);
+					}
+					peer->lastms = iaxs[fr.callno]->pingtime;
+					if (peer->pokeexpire > -1)
+						ast_sched_del(sched, peer->pokeexpire);
+					iax2_destroy_nolock(fr.callno);
+					/* Try again eventually */
+					if ((peer->lastms < 0)  || (peer->lastms > peer->maxms))
+						peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, iax2_poke_peer_s, peer);
+					else
+						peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_OK, iax2_poke_peer_s, peer);
+				}
+				break;
+			case AST_IAX2_COMMAND_LAGRQ:
+			case AST_IAX2_COMMAND_LAGRP:
+#ifdef BRIDGE_OPTIMIZATION
+				if (iaxs[fr.callno]->bridgecallno) {
+					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.samples = 0;
+					schedule_delivery(iaxfrdup2(&fr, 0), 1);
+#ifdef BRIDGE_OPTIMIZATION
+				}
+#endif				
+				break;
+			case AST_IAX2_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;
+				}
+				if (authenticate_reply(iaxs[fr.callno], &iaxs[fr.callno]->addr, &ies, iaxs[fr.callno]->secret, iaxs[fr.callno]->outkey)) {
+					ast_log(LOG_WARNING, 
+						"I don't know how to authenticate %s to %s\n", 
+						ies.username ? ies.username : "<unknown>", inet_ntoa(iaxs[fr.callno]->addr.sin_addr));
+				}
+				break;
+			case AST_IAX2_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;
+				}
+				if (authenticate_verify(iaxs[fr.callno], &ies)) {
+					ast_log(LOG_NOTICE, "Host %s failed to authenticate as %s\n", inet_ntoa(iaxs[fr.callno]->addr.sin_addr), iaxs[fr.callno]->username);
+					memset(&ied0, 0, sizeof(ied0));
+					iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No authority found");
+					send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+					break;
+				}
+				/* 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);
+				if (strcmp(iaxs[fr.callno]->exten, "TBD") && !exists) {
+					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);
+					memset(&ied0, 0, sizeof(ied0));
+					iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
+					send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+				} else {
+					/* Select an appropriate format */
+					format = iaxs[fr.callno]->peerformat & iax2_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 & iax2_capability;
+						if (!format) {
+							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, iax2_capability);
+							memset(&ied0, 0, sizeof(ied0));
+							iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
+							send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+						} else {
+							/* Pick one... */
+							format = ast_best_codec(iaxs[fr.callno]->peercapability & iax2_capability);
+							if (!format) {
+								ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[fr.callno]->peercapability & iax2_capability);
+								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, iax2_capability);
+								memset(&ied0, 0, sizeof(ied0));
+								iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
+								send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+							}
+						}
+					}
+					if (format) {
+						/* Authentication received */
+						memset(&ied1, 0, sizeof(ied1));
+						iax_ie_append_int(&ied1, IAX_IE_FORMAT, format);
+						send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -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_iax2_new(iaxs[fr.callno], AST_STATE_RING, format)))
+								iax2_destroy_nolock(fr.callno);
+						} else {
+							iaxs[fr.callno]->state |= IAX_STATE_TBD;
+							/* If this is a TBD call, we're ready but now what...  */
+							if (option_verbose > 2)
+								ast_verbose(VERBOSE_PREFIX_3 "Accepted AUTHENTICATED TBD call from %s\n", inet_ntoa(sin.sin_addr));
+						}
+					}
+				}
+				break;
+			case AST_IAX2_COMMAND_DIAL:
+				if (iaxs[fr.callno]->state & IAX_STATE_TBD) {
+					iaxs[fr.callno]->state &= ~IAX_STATE_TBD;
+					strncpy(iaxs[fr.callno]->exten, ies.called_number ? ies.called_number : "s", sizeof(iaxs[fr.callno]->exten)-1);	
+					if (!ast_exists_extension(NULL, iaxs[fr.callno]->context, iaxs[fr.callno]->exten, 1, iaxs[fr.callno]->callerid)) {
+						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);
+						memset(&ied0, 0, sizeof(ied0));
+						iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
+						send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+					} else {
+						iaxs[fr.callno]->state |= IAX_STATE_STARTED;
+						if (option_verbose > 2) 
+							ast_verbose(VERBOSE_PREFIX_3 "Accepting DIAL from %s, formats = 0x%x\n", inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformat);
+						iaxs[fr.callno]->state |= IAX_STATE_STARTED;
+						if(!(c = ast_iax2_new(iaxs[fr.callno], AST_STATE_RING, iaxs[fr.callno]->peerformat)))
+							iax2_destroy_nolock(fr.callno);
+					}
+				}
+				break;
+			case AST_IAX2_COMMAND_INVAL:
+				iaxs[fr.callno]->error = ENOTCONN;
+				ast_log(LOG_DEBUG, "Immediately destroying %d, having received INVAL\n", fr.callno);
+				iax2_destroy_nolock(fr.callno);
+				if (option_debug)
+					ast_log(LOG_DEBUG, "Destroying call %d\n", fr.callno);
+				break;
+			case AST_IAX2_COMMAND_VNAK:
+				ast_log(LOG_DEBUG, "Sending VNAK\n");
+				/* Force retransmission */
+				vnak_retransmit(fr.callno, fr.iseqno);
+				break;
+			case AST_IAX2_COMMAND_REGREQ:
+				if (register_verify(fr.callno, &sin, &ies)) {
+					memset(&ied0, 0, sizeof(ied0));
+					iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Registration Refused");
+					send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_REGREJ, 0, ied0.buf, ied0.pos, -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_IAX2_COMMAND_REGACK:
+				if (iax2_ack_registry(&ies, &sin, fr.callno)) 
+					ast_log(LOG_WARNING, "Registration failure\n");
+				iax2_destroy_nolock(fr.callno);
+				break;
+			case AST_IAX2_COMMAND_REGREJ:
+				if (iaxs[fr.callno]->reg) {
+					ast_log(LOG_NOTICE, "Registration of '%s' rejected: %s\n", iaxs[fr.callno]->reg->username, ies.cause ? ies.cause : "<unknown>");
+					iaxs[fr.callno]->reg->regstate = REG_STATE_REJECTED;
+				}
+				iax2_destroy_nolock(fr.callno);
+				break;
+			case AST_IAX2_COMMAND_REGAUTH:
+				/* Authentication request */
+				if (registry_rerequest(&ies, fr.callno, &sin)) {
+					memset(&ied0, 0, sizeof(ied0));
+					iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No authority found");
+					send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
+				}
+				break;
+			case AST_IAX2_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) {
+					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_IAX2_COMMAND_TXREJ, 0, NULL, 0, -1);
+					}
+				}
+				break;
+			case AST_IAX2_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) {
+						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);
+
+							memset(&ied0, 0, sizeof(ied0));
+							memset(&ied1, 0, sizeof(ied1));
+							iax_ie_append_short(&ied0, IAX_IE_CALLNO, iaxs[iaxs[fr.callno]->bridgecallno]->peercallno);
+							iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr.callno]->peercallno);
+							send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_TXREL, 0, ied0.buf, ied0.pos, -1);
+							send_command(iaxs[iaxs[fr.callno]->bridgecallno], AST_FRAME_IAX, AST_IAX2_COMMAND_TXREL, 0, ied1.buf, ied1.pos, -1);
+
+						}
+					}
+				}
+				break;
+			case AST_IAX2_COMMAND_TXREQ:
+				try_transfer(iaxs[fr.callno], &ies);
+				break;
+			case AST_IAX2_COMMAND_TXCNT:
+				if (iaxs[fr.callno]->transferring)
+					send_command_transfer(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_TXACC, 0, NULL, 0);
+				break;
+			case AST_IAX2_COMMAND_TXREL:
+				complete_transfer(fr.callno, &ies);
+				break;	
+			case AST_IAX2_COMMAND_DPREP:
+				complete_dpreply(iaxs[fr.callno], &ies);
+				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 */
+			if ((f.subclass != AST_IAX2_COMMAND_ACK) && 
+			  (f.subclass != AST_IAX2_COMMAND_TXCNT) && 
+			  (f.subclass != AST_IAX2_COMMAND_TXACC) && 
+			  (f.subclass != AST_IAX2_COMMAND_INVAL) &&
+			  (f.subclass != AST_IAX2_COMMAND_VNAK)) { 
+			  	if (iaxs[fr.callno] && iaxs[fr.callno]->aseqno != iaxs[fr.callno]->iseqno)
+					send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
+			}
+			ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
+			return 1;
+		}
+		/* Unless this is an ACK or INVAL frame, ack it */
+		if (iaxs[fr.callno]->aseqno != iaxs[fr.callno]->iseqno)
+			send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX2_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
+	} else {
+		/* A mini frame */
+		f.frametype = AST_FRAME_VOICE;
+		if (iaxs[fr.callno]->voiceformat > 0)
+			f.subclass = iaxs[fr.callno]->voiceformat;
+		else {
+			ast_log(LOG_WARNING, "Received mini frame before first full voice frame\n ");
+			iax2_vnak(fr.callno);
+			ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
+			return 1;
+		}
+		f.datalen = res - sizeof(struct ast_iax2_mini_hdr);
+		if (f.datalen < 0) {
+			ast_log(LOG_WARNING, "Datalen < 0?\n");
+			ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
+			return 1;
+		}
+		if (f.datalen)
+			f.data = buf + sizeof(struct ast_iax2_mini_hdr);
+		else
+			f.data = NULL;
+		fr.ts = (iaxs[fr.callno]->last & 0xFFFF0000L) | ntohs(mh->ts);
+	}
+	/* Don't pass any packets until we're started */
+	if (!(iaxs[fr.callno]->state & IAX_STATE_STARTED)) {
+		ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
+		return 1;
+	}
+	/* Common things */
+	snprintf(src, sizeof(src), "IAX2/%s/%d", inet_ntoa(sin.sin_addr),fr.callno);	f.src = src;
+	f.mallocd = 0;
+	f.offset = 0;
+	fr.f = &f;
+	if (f.datalen && (f.frametype == AST_FRAME_VOICE)) 
+		f.samples = get_samples(&f);
+	else
+		f.samples = 0;
+
+	/* If this is our most recent packet, use it as our basis for timestamping */
+	if (iaxs[fr.callno]->last < fr.ts) {
+		iaxs[fr.callno]->last = fr.ts;
+		fr.outoforder = 0;
+	} else {
+		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;
+	}
+#ifdef BRIDGE_OPTIMIZATION
+	if (iaxs[fr.callno]->bridgecallno) {
+		forward_delivery(&fr);
+	} else {
+		schedule_delivery(iaxfrdup2(&fr, 0), 1);
+	}
+#else
+	schedule_delivery(iaxfrdup2(&fr, 0), 1);
+#endif
+	/* Always run again */
+	ast_pthread_mutex_unlock(&iaxsl[fr.callno]);
+	return 1;
+}
+
+static int iax2_do_register(struct iax2_registry *reg)
+{
+	struct iax_ie_data ied;
+	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(0, 0, &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, iax2_do_register_s, reg);
+	/* Send the request */
+	memset(&ied, 0, sizeof(ied));
+	iax_ie_append_str(&ied, IAX_IE_USERNAME, reg->username);
+	iax_ie_append_short(&ied, IAX_IE_REFRESH, reg->refresh);
+	send_command(iaxs[reg->callno],AST_FRAME_IAX, AST_IAX2_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1);
+	reg->regstate = REG_STATE_REGSENT;
+	return 0;
+}
+
+
+static int iax2_poke_noanswer(void *data)
+{
+	struct iax2_peer *peer = data;
+	peer->pokeexpire = -1;
+	if (peer->lastms > -1)
+		ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE!\n", peer->name);
+	if (peer->callno > 0)
+		iax2_destroy(peer->callno);
+	peer->callno = 0;
+	peer->lastms = -1;
+	/* Try again quickly */
+	peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, iax2_poke_peer_s, peer);
+	return 0;
+}
+
+static int iax2_poke_peer(struct iax2_peer *peer)
+{
+	if (!peer->maxms || !peer->addr.sin_addr.s_addr) {
+		/* IF we have no IP, or this isn't to be monitored, return
+		  imeediately after clearing things out */
+		peer->lastms = 0;
+		peer->pokeexpire = -1;
+		peer->callno = 0;
+		return 0;
+	}
+	if (peer->callno > 0) {
+		ast_log(LOG_NOTICE, "Still have a callno...\n");
+		iax2_destroy(peer->callno);
+	}
+	peer->callno = find_callno(0, 0, &peer->addr, NEW_FORCE);
+	if (peer->callno < 0) {
+		ast_log(LOG_WARNING, "Unable to allocate call for poking peer '%s'\n", peer->name);
+		return -1;
+	}
+	if (peer->pokeexpire > -1)
+		ast_sched_del(sched, peer->pokeexpire);
+	/* Speed up retransmission times */
+	iaxs[peer->callno]->pingtime = peer->maxms / 4 + 1;
+	iaxs[peer->callno]->peerpoke = peer;
+	send_command(iaxs[peer->callno], AST_FRAME_IAX, AST_IAX2_COMMAND_POKE, 0, NULL, 0, -1);
+	peer->pokeexpire = ast_sched_add(sched, DEFAULT_MAXMS * 2, iax2_poke_noanswer, peer);
+	return 0;
+}
+
+static void free_context(struct iax2_context *con)
+{
+	struct iax2_context *conl;
+	while(con) {
+		conl = con;
+		con = con->next;
+		free(conl);
+	}
+}
+
+static struct ast_channel *iax2_request(char *type, int format, void *data)
+{
+	int callno;
+	int res;
+	int sendani;
+	int maxtime;
+	int fmt, native;
+	struct sockaddr_in sin;
+	char s[256];
+	char *st;
+	struct ast_channel *c;
+	char *stringp=NULL;
+	int capability = iax2_capability;
+	strncpy(s, (char *)data, sizeof(s)-1);
+	/* FIXME The next two lines seem useless */
+	stringp=s;
+	strsep(&stringp, "/");
+
+	stringp=s;
+	strsep(&stringp, "@");
+	st = strsep(&stringp, "@");
+	if (!st)
+		st = s;
+	/* Populate our address from the given */
+	if (create_addr(&sin, &capability, &sendani, &maxtime, st, NULL)) {
+		return NULL;
+	}
+	callno = find_callno(0, 0, &sin, NEW_FORCE);
+	if (callno < 0) {
+		ast_log(LOG_WARNING, "Unable to create call\n");
+		return NULL;
+	}
+	ast_pthread_mutex_lock(&iaxsl[callno]);
+	/* Keep track of sendani flag */
+	iaxs[callno]->sendani = sendani;
+	iaxs[callno]->maxtime = maxtime;
+	c = ast_iax2_new(iaxs[callno], AST_STATE_DOWN, capability);
+	ast_pthread_mutex_unlock(&iaxsl[callno]);
+	if (c) {
+		/* Choose a format we can live with */
+		if (c->nativeformats & format)
+			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);
+				return NULL;
+			}
+			c->nativeformats = native;
+		}
+	}
+	return c;
+}
+
+static void *network_thread(void *ignore)
+{
+	/* Our job is simple: Send queued messages, retrying if necessary.  Read frames 
+	   from the network, and queue them for delivery to the channels */
+	int res;
+	struct ast_iax2_frame *f, *freeme;
+	/* Establish I/O callback for socket read */
+	ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
+	for(;;) {
+		/* Go through the queue, sending messages which have not yet been
+		   sent, and scheduling retransmissions if appropriate */
+		ast_pthread_mutex_lock(&iaxq.lock);
+		f = iaxq.head;
+		while(f) {
+			freeme = NULL;
+			if (!f->sentyet) {
+				f->sentyet++;
+				/* Send a copy immediately -- errors here are ok, so don't bother locking */
+				if (iaxs[f->callno]) {
+					send_packet(f);
+				} 
+				if (f->retries < 0) {
+					/* This is not supposed to be retransmitted */
+					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--;
+					/* 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++;
+					f->retrans = ast_sched_add(sched, f->retrytime, attempt_transmit, f);
+				}
+			}
+			f = f->next;
+			if (freeme)
+				ast_iax2_frame_free(freeme);
+		}
+		ast_pthread_mutex_unlock(&iaxq.lock);
+		res = ast_sched_wait(sched);
+		if ((res > 1000) || (res < 0))
+			res = 1000;
+		res = ast_io_wait(io, res);
+		if (res >= 0) {
+			ast_sched_runq(sched);
+		}
+	}
+	return NULL;
+}
+
+static int start_network_thread(void)
+{
+	return pthread_create(&netthreadid, NULL, network_thread, NULL);
+}
+
+static struct iax2_context *build_context(char *context)
+{
+	struct iax2_context *con = malloc(sizeof(struct iax2_context));
+	if (con) {
+		strncpy(con->context, context, sizeof(con->context)-1);
+		con->next = NULL;
+	}
+	return con;
+}
+
+static int get_auth_methods(char *value)
+{
+	int methods = 0;
+	if (strstr(value, "rsa"))
+		methods |= IAX_AUTH_RSA;
+	if (strstr(value, "md5"))
+		methods |= IAX_AUTH_MD5;
+	if (strstr(value, "plaintext"))
+		methods |= IAX_AUTH_RSA;
+	return methods;
+}
+
+static struct iax2_peer *build_peer(char *name, struct ast_variable *v)
+{
+	struct iax2_peer *peer;
+	struct iax2_peer *prev;
+	int maskfound=0;
+	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 iax2_peer));
+		memset(peer, 0, sizeof(struct iax2_peer));
+		peer->expire = -1;
+		peer->pokeexpire = -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 = iax2_capability;
+		while(v) {
+			if (!strcasecmp(v->name, "secret")) 
+				strncpy(peer->secret, v->value, sizeof(peer->secret)-1);
+			else if (!strcasecmp(v->name, "auth")) {
+				peer->authmethods = get_auth_methods(v->value);
+			} else if (!strcasecmp(v->name, "host")) {
+				if (!strcasecmp(v->value, "dynamic")) {
+					/* They'll register with us */
+					peer->dynamic = 1;
+					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;
+					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, "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, "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));
+			} else if (!strcasecmp(v->name, "qualify")) {
+				if (!strcasecmp(v->value, "no")) {
+					peer->maxms = 0;
+				} else if (!strcasecmp(v->value, "yes")) {
+					peer->maxms = DEFAULT_MAXMS;
+				} else if (sscanf(v->value, "%d", &peer->maxms) != 1) {
+					ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of iax.conf\n", peer->name, v->lineno);
+					peer->maxms = 0;
+				}
+			}
+
+			v=v->next;
+		}
+		if (!peer->authmethods)
+			peer->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT;
+		peer->delme = 0;
+	}
+	return peer;
+}
+
+static struct iax2_user *build_user(char *name, struct ast_variable *v)
+{
+	struct iax2_user *user;
+	struct iax2_context *con, *conl = NULL;
+	int format;
+	user = (struct iax2_user *)malloc(sizeof(struct iax2_user));
+	if (user) {
+		memset(user, 0, sizeof(struct iax2_user));
+		strncpy(user->name, name, sizeof(user->name)-1);
+		while(v) {
+			if (!strcasecmp(v->name, "context")) {
+				con = build_context(v->value);
+				if (con) {
+					if (conl)
+						conl->next = con;
+					else
+						user->contexts = con;
+					conl = con;
+				}
+			} else if (!strcasecmp(v->name, "permit") ||
+					   !strcasecmp(v->name, "deny")) {
+				user->ha = ast_append_ha(v->name, v->value, user->ha);
+			} else if (!strcasecmp(v->name, "auth")) {
+				user->authmethods = get_auth_methods(v->value);
+			} else if (!strcasecmp(v->name, "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 (user->authmethods) {
+		if (strlen(user->secret)) {
+			user->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT;
+			if (strlen(user->inkeys))
+				user->authmethods |= IAX_AUTH_RSA;
+		} else if (strlen(user->inkeys)) {
+			user->authmethods = IAX_AUTH_RSA;
+		} else {
+			user->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT;
+		}
+	}
+	return user;
+}
+
+
+void delete_users(void){
+	struct iax2_user *user, *userlast;
+	struct iax2_peer *peer;
+	struct iax2_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 iax2_peer *peer, *peerlast, *peernext;
+	int x;
+	ast_pthread_mutex_lock(&peerl.lock);
+	peerlast = NULL;
+	for (peer=peerl.peers;peer;) {
+		peernext = peer->next;
+		if (peer->delme) {
+			for (x=0;x<AST_IAX2_MAX_CALLS;x++) {
+				ast_pthread_mutex_lock(&iaxsl[x]);
+				if (iaxs[x] && (iaxs[x]->peerpoke == peer)) {
+					iax2_destroy(x);
+				}
+				ast_pthread_mutex_unlock(&iaxsl[x]);
+			}
+			/* Delete it, it needs to disappear */
+			if (peer->expire > -1)
+				ast_sched_del(sched, peer->expire);
+			if (peer->pokeexpire > -1)
+				ast_sched_del(sched, peer->pokeexpire);
+			if (peer->callno > 0)
+				iax2_destroy(peer->callno);
+			free(peer);
+			if (peerlast)
+				peerlast->next = peernext;
+			else
+				peerl.peers = peernext;
+		} else
+			peerlast = peer;
+		peer=peernext;
+	}
+	ast_pthread_mutex_unlock(&peerl.lock);
+}
+
+
+static int set_config(char *config_file, struct sockaddr_in* sin){
+	struct ast_config *cfg;
+	int capability=iax2_capability;
+	struct ast_variable *v;
+	char *cat;
+	char *utype;
+	int format;
+	struct iax2_user *user;
+	struct iax2_peer *peer;
+	static unsigned short int last_port=0;
+
+	cfg = ast_load(config_file);
+	
+	if (!cfg) {
+		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")){ 
+				ast_log(LOG_WARNING, "Ignoring port for now\n");
+#if 0				
+			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");
+#endif				
+		}
+		else if (!strcasecmp(v->name, "pingtime")) 
+			ping_time = atoi(v->value);
+		else if (!strcasecmp(v->name, "maxjitterbuffer")) 
+			maxjitterbuffer = atoi(v->value);
+		else if (!strcasecmp(v->name, "maxexcessbuffer")) 
+			max_jitter_buffer = atoi(v->value);
+		else if (!strcasecmp(v->name, "lagrqtime")) 
+			lagrq_time = atoi(v->value);
+		else if (!strcasecmp(v->name, "dropcount")) 
+			iax2_dropcount = atoi(v->value);
+		else if (!strcasecmp(v->name, "bindaddr"))
+			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")) {
+				capability = IAX_CAPABILITY_LOWBANDWIDTH;
+			} else if (!strcasecmp(v->value, "medium")) {
+				capability = IAX_CAPABILITY_MEDBANDWIDTH;
+			} else if (!strcasecmp(v->value, "high")) {
+				capability = IAX_CAPABILITY_FULLBANDWIDTH;
+			} else
+				ast_log(LOG_WARNING, "bandwidth must be either low, medium, or high\n");
+		} 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
+				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
+				capability &= ~format;
+		} else if (!strcasecmp(v->name, "register")) {
+			iax2_register(v->value, v->lineno);
+		} else if (!strcasecmp(v->name, "tos")) {
+			if (sscanf(v->value, "%i", &format) == 1)
+				tos = format & 0xff;
+			else if (!strcasecmp(v->value, "lowdelay"))
+				tos = IPTOS_LOWDELAY;
+			else if (!strcasecmp(v->value, "throughput"))
+				tos = IPTOS_THROUGHPUT;
+			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;
+	}
+	iax2_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") || !strcasecmp(utype, "friend")) {
+					user = build_user(cat, ast_variable_browse(cfg, cat));
+					if (user) {
+						ast_pthread_mutex_lock(&userl.lock);
+						user->next = userl.users;
+						userl.users = user;
+						ast_pthread_mutex_unlock(&userl.lock);
+					}
+				}
+				if (!strcasecmp(utype, "peer") || !strcasecmp(utype, "friend")) {
+					peer = build_peer(cat, ast_variable_browse(cfg, cat));
+					if (peer) {
+						ast_pthread_mutex_lock(&peerl.lock);
+						peer->next = peerl.peers;
+						peerl.peers = peer;
+						ast_pthread_mutex_unlock(&peerl.lock);
+					}
+				} 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);
+		}
+		cat = ast_category_browse(cfg, cat);
+	}
+	ast_destroy(cfg);
+	return capability;
+}
+
+static int reload_config(void)
+{
+	char *config = "iax.conf";
+	struct iax2_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)
+		iax2_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;
+	int callno;
+	struct iax_ie_data ied;
+	for (x=0;x<AST_IAX2_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 (!pthread_mutex_trylock(&iaxsl[x])) {
+			if (iaxs[x] && !strcasecmp(data, iaxs[x]->dproot)) {
+				ast_pthread_mutex_unlock(&iaxsl[x]);
+				return x;
+			}
+			ast_pthread_mutex_unlock(&iaxsl[x]);
+		}
+	}
+	memset(&ied, 0, sizeof(ied));
+	/* 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, NULL, host, NULL)) {
+		return -1;
+	}
+	ast_log(LOG_DEBUG, "host: %s, user: %s, password: %s, context: %s\n", host, username, password, context);
+	callno = find_callno(0, 0, &sin, NEW_FORCE);
+	if (callno < 1) {
+		ast_log(LOG_WARNING, "Unable to create call\n");
+		return -1;
+	}
+	ast_pthread_mutex_lock(&iaxsl[callno]);
+	strncpy(iaxs[callno]->dproot, data, sizeof(iaxs[callno]->dproot)-1);
+	iaxs[callno]->capability = IAX_CAPABILITY_FULLBANDWIDTH;
+
+	iax_ie_append_short(&ied, IAX_IE_VERSION, AST_IAX2_PROTO_VERSION);
+	iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, "TBD");
+	if (context)
+		iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, context);
+	if (username)
+		iax_ie_append_str(&ied, IAX_IE_USERNAME, username);
+	iax_ie_append_int(&ied, IAX_IE_FORMAT, IAX_CAPABILITY_FULLBANDWIDTH);
+	iax_ie_append_int(&ied, IAX_IE_CAPABILITY, IAX_CAPABILITY_FULLBANDWIDTH);
+	/* Keep password handy */
+	if (password)
+		strncpy(iaxs[callno]->secret, password, sizeof(iaxs[callno]->secret)-1);
+#if 0
+	/* XXX Need equivalent XXX */
+	if (option_verbose > 2)
+		ast_verbose(VERBOSE_PREFIX_3 "Calling TBD using options '%s'\n", requeststr);
+#endif		
+	/* Start the call going */
+	send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX2_COMMAND_NEW, 0, ied.buf, ied.pos, -1);
+	ast_pthread_mutex_unlock(&iaxsl[callno]);
+	return callno;
+}
+
+static struct iax2_dpcache *find_cache(struct ast_channel *chan, char *data, char *context, char *exten, int priority)
+{
+	struct iax2_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) {
+					/* 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 iax2_dpcache));
+		if (!dp)
+			return NULL;
+		memset(dp, 0, sizeof(struct iax2_dpcache));
+		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)
+			iax2_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 iax2_exists(struct ast_channel *chan, char *context, char *exten, int priority, char *callerid, char *data)
+{
+	struct iax2_dpcache *dp;
+	int res = 0;
+#if 0
+	ast_log(LOG_NOTICE, "iax2_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 iax2_canmatch(struct ast_channel *chan, char *context, char *exten, int priority, char *callerid, char *data)
+{
+	int res = 0;
+	struct iax2_dpcache *dp;
+#if 0
+	ast_log(LOG_NOTICE, "iax2_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 iax2_matchmore(struct ast_channel *chan, char *context, char *exten, int priority, char *callerid, char *data)
+{
+	int res = 0;
+	struct iax2_dpcache *dp;
+#if 0
+	ast_log(LOG_NOTICE, "iax2_matchmore: con: %s, exten: %s, pri: %d, cid: %s, data: %s\n", context, exten, priority, callerid ? callerid : "<unknown>", data);
+#endif
+	if (priority != 1)
+		return 0;
+	ast_pthread_mutex_lock(&dpcache_lock);
+	dp = find_cache(chan, data, context, exten, priority);
+	if (dp) {
+		if (dp->flags & CACHE_FLAG_MATCHMORE)
+			res= 1;
+	}
+	ast_pthread_mutex_unlock(&dpcache_lock);
+	if (!dp) {
+		ast_log(LOG_WARNING, "Unable to make DP cache\n");
+	}
+	return res;
+}
+
+static int iax2_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 iax2_dpcache *dp;
+	struct ast_app *dial;
+#if 0
+	ast_log(LOG_NOTICE, "iax2_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 iax2_switch = 
+{
+	name: 			"IAX2",
+	description: 	"IAX2 Remote Dialplan Switch",
+	exists:			iax2_exists,
+	canmatch:		iax2_canmatch,
+	exec:			iax2_exec,
+	matchmore:		iax2_matchmore,
+};
+
+int load_module(void)
+{
+	char *config = "iax.conf";
+	int res = 0;
+	int x;
+	struct iax2_registry *reg;
+	struct iax2_peer *peer;
+	
+	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;
+
+	for (x=0;x<AST_IAX2_MAX_CALLS;x++)
+		ast_pthread_mutex_init(&iaxsl[x]);
+	
+	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);
+
+	ast_manager_register( "IAX2peers", 0, manager_iax2_show_peers, "List IAX Peers" );
+
+	set_config(config,&sin);
+
+	if (ast_channel_register(type, tdesc, iax2_capability, iax2_request)) {
+		ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
+		unload_module();
+		return -1;
+	}
+
+	if (ast_register_switch(&iax2_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,(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) 
+			ast_verbose(VERBOSE_PREFIX_2 "IAX Ready and Listening on %s port %d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+	} else {
+		ast_log(LOG_ERROR, "Unable to start network thread\n");
+		close(netsocket);
+	}
+	for (reg = registrations; reg; reg = reg->next)
+		iax2_do_register(reg);
+	ast_pthread_mutex_lock(&peerl.lock);
+	for (peer = peerl.peers; peer; peer = peer->next)
+		iax2_poke_peer(peer);
+	ast_pthread_mutex_unlock(&peerl.lock);
+	return res;
+}
+
+char *description()
+{
+	return desc;
+}
+
+int unload_module()
+{
+	int x;
+	/* Cancel the network thread, close the net socket */
+	pthread_cancel(netthreadid);
+	pthread_join(netthreadid, NULL);
+	close(netsocket);
+	for (x=0;x<AST_IAX2_MAX_CALLS;x++)
+		if (iaxs[x])
+			iax2_destroy(x);
+	ast_manager_unregister( "IAXpeers" );
+	ast_cli_unregister(&cli_show_users);
+	ast_cli_unregister(&cli_show_channels);
+	ast_cli_unregister(&cli_show_peers);
+	ast_cli_unregister(&cli_set_jitter);
+	ast_cli_unregister(&cli_show_stats);
+	ast_cli_unregister(&cli_show_cache);
+	ast_unregister_switch(&iax2_switch);
+	delete_users();
+	return 0;
+}
+
+int usecount()
+{
+	int res;
+	ast_pthread_mutex_lock(&usecnt_lock);
+	res = usecnt;
+	ast_pthread_mutex_unlock(&usecnt_lock);
+	return res;
+}
+
+char *key()
+{
+	return ASTERISK_GPL_KEY;
+}
diff --git a/channels/chan_oss.c b/channels/chan_oss.c
index f5fcea1266ae4791d1a98bec67e37884e04dce82..a5de53844a72be87ef14dc952c8b52464b252b25 100755
--- a/channels/chan_oss.c
+++ b/channels/chan_oss.c
@@ -819,7 +819,7 @@ static int console_sendtext(int fd, int argc, char *argv[])
 	if (strlen(text2send))
 		ast_cli(fd, "Warning: message already waiting to be sent, overwriting\n");
 	strcpy(text2send, "");
-	while(tmparg <= argc) {
+	while(tmparg < argc) {
 		strncat(text2send, argv[tmparg++], sizeof(text2send) - strlen(text2send));
 		strncat(text2send, " ", sizeof(text2send) - strlen(text2send));
 	}
diff --git a/channels/iax2.h b/channels/iax2.h
new file mode 100755
index 0000000000000000000000000000000000000000..c3727ac81363e0d237603d9e566dda90b750d5f2
--- /dev/null
+++ b/channels/iax2.h
@@ -0,0 +1,126 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Implementation of Inter-Asterisk eXchange
+ * 
+ * Copyright (C) 2003, Digium
+ *
+ * Mark Spencer <markster@linux-support.net>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+ 
+#ifndef _ASTERISK_IAX2_H
+#define _ASTERISK_IAX2_H
+
+/* Max version of IAX protocol we support */
+#define AST_IAX2_PROTO_VERSION 2
+
+#define AST_IAX2_MAX_CALLS 32768
+
+#define AST_FLAG_FULL		0x8000
+
+#define AST_FLAG_RETRANS	0x8000
+
+#define AST_FLAG_SC_LOG		0x80
+
+#define AST_MAX_SHIFT		0x1F
+
+#define AST_IAX2_WINDOW		256
+
+/* Subclass for AST_FRAME_IAX */
+#define AST_IAX2_COMMAND_NEW		1
+#define AST_IAX2_COMMAND_PING	2
+#define AST_IAX2_COMMAND_PONG	3
+#define AST_IAX2_COMMAND_ACK		4
+#define AST_IAX2_COMMAND_HANGUP	5
+#define AST_IAX2_COMMAND_REJECT	6
+#define AST_IAX2_COMMAND_ACCEPT	7
+#define AST_IAX2_COMMAND_AUTHREQ	8
+#define AST_IAX2_COMMAND_AUTHREP	9
+#define AST_IAX2_COMMAND_INVAL	10
+#define AST_IAX2_COMMAND_LAGRQ	11
+#define AST_IAX2_COMMAND_LAGRP	12
+#define AST_IAX2_COMMAND_REGREQ	13	/* Registration request */
+#define AST_IAX2_COMMAND_REGAUTH	14	/* Registration authentication required */
+#define AST_IAX2_COMMAND_REGACK	15	/* Registration accepted */
+#define AST_IAX2_COMMAND_REGREJ	16	/* Registration rejected */
+#define AST_IAX2_COMMAND_REGREL	17	/* Force release of registration */
+#define AST_IAX2_COMMAND_VNAK	18	/* If we receive voice before valid first voice frame, send this */
+#define AST_IAX2_COMMAND_DPREQ	19	/* Request status of a dialplan entry */
+#define AST_IAX2_COMMAND_DPREP	20	/* Request status of a dialplan entry */
+#define AST_IAX2_COMMAND_DIAL	21	/* Request a dial on channel brought up TBD */
+#define AST_IAX2_COMMAND_TXREQ	22	/* Transfer Request */
+#define AST_IAX2_COMMAND_TXCNT	23	/* Transfer Connect */
+#define AST_IAX2_COMMAND_TXACC	24	/* Transfer Accepted */
+#define AST_IAX2_COMMAND_TXREADY	25	/* Transfer ready */
+#define AST_IAX2_COMMAND_TXREL	26	/* Transfer release */
+#define AST_IAX2_COMMAND_TXREJ	27	/* Transfer reject */
+#define AST_IAX2_COMMAND_QUELCH	28	/* Stop audio/video transmission */
+#define AST_IAX2_COMMAND_UNQUELCH 29	/* Resume audio/video transmission */
+#define AST_IAX2_COMMAND_POKE    30  /* Like ping, but does not require an open connection */
+#define AST_IAX2_COMMAND_PAGE	31	/* Paging description */
+#define AST_IAX2_COMMAND_MWI	32	/* Stand-alone message waiting indicator */
+#define AST_IAX2_COMMAND_UNSUPPORT	33	/* Unsupported message received */
+
+#define AST_DEFAULT_REG_EXPIRE  60	/* By default require re-registration once per minute */
+
+#define AST_DEFAULT_IAX_PORTNO	4569
+
+/* IAX Information elements */
+#define IAX_IE_CALLED_NUMBER		1		/* Number/extension being called - string */
+#define IAX_IE_CALLING_NUMBER		2		/* Calling number - string */
+#define IAX_IE_CALLING_ANI			3		/* Calling number ANI for billing  - string */
+#define IAX_IE_CALLING_NAME			4		/* Name of caller - string */
+#define IAX_IE_CALLED_CONTEXT		5		/* Context for number - string */
+#define IAX_IE_USERNAME				6		/* Username (peer or user) for authentication - string */
+#define IAX_IE_PASSWORD				7		/* Password for authentication - string */
+#define IAX_IE_CAPABILITY			8		/* Actual codec capability - unsigned int */
+#define IAX_IE_FORMAT				9		/* Desired codec format - unsigned int */
+#define IAX_IE_LANGUAGE				10		/* Desired language - string */
+#define IAX_IE_VERSION				11		/* Protocol version - short */
+#define IAX_IE_ADSICPE				12		/* CPE ADSI capability - int */
+#define IAX_IE_DNID					13		/* Originally dialed DNID - string */
+#define IAX_IE_AUTHMETHODS			14		/* Authentication method(s) - short */
+#define IAX_IE_CHALLENGE			15		/* Challenge data for MD5/RSA - string */
+#define IAX_IE_MD5_RESULT			16		/* MD5 challenge result - string */
+#define IAX_IE_RSA_RESULT			17		/* RSA challenge result - string */
+#define IAX_IE_APPARENT_ADDR		18		/* Apparent address of peer - struct sockaddr_in */
+#define IAX_IE_REFRESH				19		/* When to refresh registration - short */
+#define IAX_IE_DPSTATUS				20		/* Dialplan status - short */
+#define IAX_IE_CALLNO				21		/* Call number of peer - short */
+#define IAX_IE_CAUSE				22		/* Cause - string */
+
+#define IAX_AUTH_PLAINTEXT			(1 << 0)
+#define IAX_AUTH_MD5				(1 << 1)
+#define IAX_AUTH_RSA				(1 << 2)
+
+#define IAX_DPSTATUS_EXISTS			(1 << 0)
+#define IAX_DPSTATUS_CANEXIST		(1 << 1)
+#define IAX_DPSTATUS_NONEXISTANT	(1 << 2)
+#define IAX_DPSTATUS_IGNOREPAT		(1 << 14)
+#define IAX_DPSTATUS_MATCHMORE		(1 << 15)
+
+/* Full frames are always delivered reliably */
+struct ast_iax2_full_hdr {
+	unsigned short scallno;	/* Source call number -- high bit must be 1 */
+	unsigned short dcallno;	/* Destination call number -- high bit is 1 if retransmission */
+	unsigned int ts;		/* 32-bit timestamp in milliseconds (from 1st transmission) */
+	unsigned short oseqno;	/* Packet number (outgoing) */
+	unsigned short iseqno;	/* Packet number (next incoming expected) */
+	char type;				/* Frame type */
+	unsigned char csub;		/* Compressed subclass */
+	unsigned char iedata[0];
+};
+
+/* Mini header is used only for voice frames -- delivered unreliably */
+struct ast_iax2_mini_hdr {
+	short callno;			/* Source call number -- high bit must be 0 */
+	unsigned short ts;		/* 16-bit Timestamp (high 16 bits from last ast_iax2_full_hdr) */
+							/* Frametype implicitly VOICE_FRAME */
+							/* subclass implicit from last ast_iax2_full_hdr */
+	unsigned char iedata[0];
+};
+
+#endif
diff --git a/codecs/codec_gsm.c b/codecs/codec_gsm.c
index 6a3ff8054b3cc4e8b8e614edef08c99859cf23ad..42d70f432deb3d96f20e7346620fa4df45b4772d 100755
--- a/codecs/codec_gsm.c
+++ b/codecs/codec_gsm.c
@@ -196,6 +196,8 @@ static struct ast_frame *lintogsm_frameout(struct ast_translator_pvt *tmp)
 
 static void gsm_destroy_stuff(struct ast_translator_pvt *pvt)
 {
+	if (pvt->gsm)
+		gsm_destroy(pvt->gsm);
 	free(pvt);
 	localusecnt--;
 }
diff --git a/pbx.c b/pbx.c
index a7ed72064aec4714122c290b67f0af498ea5deef..0f787513cd377b216c87e07c65bbd4032a7939cb 100755
--- a/pbx.c
+++ b/pbx.c
@@ -1135,7 +1135,8 @@ int ast_pbx_run(struct ast_channel *c)
 				c->name, c->context, c->exten, c->priority);			
 			if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->callerid))) {
 				/* Something bad happened, or a hangup has been requested. */
-				if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F'))) {
+				if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
+					(res == '*') || (res == '#')) {
 					ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
 					exten[pos++] = digit = res;
 					break;