Version 0.1.2 from FTP
authorMark Spencer <markster@digium.com>
Thu, 6 Jan 2000 13:24:57 +0000 (13:24 +0000)
committerMark Spencer <markster@digium.com>
Thu, 6 Jan 2000 13:24:57 +0000 (13:24 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@197 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_iax.c [new file with mode: 0755]
configs/iax.conf.sample [new file with mode: 0755]

diff --git a/channels/chan_iax.c b/channels/chan_iax.c
new file mode 100755 (executable)
index 0000000..f3e7d33
--- /dev/null
@@ -0,0 +1,2238 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Implementation of Inter-Asterisk eXchange
+ * 
+ * Copyright (C) 1999, Mark Spencer
+ *
+ * Mark Spencer <markster@linux-support.net>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+#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 <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.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 "iax.h"
+
+#define DEFAULT_RETRY_TIME 1000
+#define MEMORY_SIZE 100
+#define DEFAULT_DROP 3
+
+/* If you want to use the simulator, then define IAX_SIMULATOR.  */
+
+/*
+#define IAX_SIMULATOR
+*/
+static char *desc = "Inter Asterisk eXchange";
+static char *tdesc = "Inter Asterisk eXchange Drver";
+static char *type = "IAX";
+static char *config = "iax.conf";
+
+static char context[80] = "default";
+
+static int max_retries = 4;
+static int ping_time = 20;
+static int lagrq_time = 10;
+static int nextcallno = 0;
+static int maxjitterbuffer=4000;
+
+static int netsocket = -1;
+
+static int usecnt;
+static pthread_mutex_t usecnt_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t iaxs_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Ethernet, etc */
+#define IAX_CAPABILITY_FULLBANDWIDTH   0x7FFFFFFF
+/* 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)
+
+static struct io_context *io;
+static struct sched_context *sched;
+
+static int iax_capability = IAX_CAPABILITY_FULLBANDWIDTH;
+
+static int iax_dropcount = DEFAULT_DROP;
+
+static int use_jitterbuffer = 1;
+
+static pthread_t netthreadid;
+
+#define IAX_STATE_STARTED              (1 << 0)
+#define IAX_STATE_AUTHENTICATED (1 << 1)
+
+#define IAX_SENSE_DENY                 0
+#define IAX_SENSE_ALLOW                        1
+
+struct iax_ha {
+       /* Host access rule */
+       struct in_addr netaddr;
+       struct in_addr netmask;
+       int sense;
+       struct iax_ha *next;
+};
+
+struct iax_context {
+       char context[AST_MAX_EXTENSION];
+       struct iax_context *next;
+};
+
+struct iax_user {
+       char name[80];
+       char secret[80];
+       char methods[80];
+       struct iax_ha *ha;
+       struct iax_context *contexts;
+       struct iax_user *next;
+};
+
+struct iax_peer {
+       char name[80];
+       char username[80];
+       char secret[80];
+       struct sockaddr_in addr;
+       struct in_addr mask;
+       struct iax_peer *next;
+};
+       
+/* 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_iax_pvt {
+       /* Pipes for communication.  pipe[1] belongs to the
+          network thread (write), and pipe[0] belongs to the individual 
+          channel (read) */
+       int pipe[2];
+       /* Last received voice format */
+       int voiceformat;
+       /* Last sent voice format */
+       int svoiceformat;
+       /* 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;
+       /* Peer Address */
+       struct sockaddr_in addr;
+       /* Our call number */
+       int callno;
+       /* Peer callno */
+       int peercallno;
+       /* Peer supported formats */
+       int peerformats;
+       /* 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;
+       /* LAG */
+       int lag;
+       /* Error, as discovered by the manager */
+       int error;
+       /* Onwer if we have one */
+       struct ast_channel *owner;
+       /* What's our state? */
+       int state;
+       /* Next outgoing sequence number */
+       unsigned short oseqno;
+       /* Next incoming sequence number */
+       unsigned short iseqno;
+       /* Peer name */
+       char peer[80];
+       /* Default Context */
+       char context[80];
+       /* Caller ID if available */
+       char callerid[80];
+       /* DNID */
+       char dnid[80];
+       /* Requested Extension */
+       char exten[AST_MAX_EXTENSION];
+       /* Expected Username */
+       char username[80];
+       /* Expected Secret */
+       char secret[80];
+       /* permitted authentication methods */
+       char methods[80];
+       /* MD5 challenge */
+       char challenge[10];
+};
+
+struct ast_iax_frame {
+       /* Actual, isolated frame */
+       struct ast_frame *f;
+       /* /Our/ call number */
+       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;
+       /* Packet sequence number */
+       int seqno;
+       /* Easy linking */
+       struct ast_iax_frame *next;
+       struct ast_iax_frame *prev;
+};
+
+static struct ast_iax_queue {
+       struct ast_iax_frame *head;
+       struct ast_iax_frame *tail;
+       int count;
+       pthread_mutex_t lock;
+} iaxq;
+
+static struct ast_user_list {
+       struct iax_user *users;
+       pthread_mutex_t lock;
+} userl;
+
+static struct ast_peer_list {
+       struct iax_peer *peers;
+       pthread_mutex_t lock;
+} peerl;
+
+/* XXX We probably should use a mutex when working with this XXX */
+static struct chan_iax_pvt *iaxs[AST_IAX_MAX_CALLS];
+
+static int send_command(struct chan_iax_pvt *, char, int, unsigned int, char *, int, int);
+
+static unsigned int calc_timestamp(struct chan_iax_pvt *p, unsigned int ts);
+
+static int send_ping(void *data)
+{
+       int callno = (long)data;
+       if (iaxs[callno]) {
+               send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_PING, 0, NULL, 0, -1);
+               return 1;
+       } else
+               return 0;
+}
+
+static int send_lagrq(void *data)
+{
+       int callno = (long)data;
+       if (iaxs[callno]) {
+               send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX_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)
+               return 1 << (csub & ~AST_FLAG_SC_LOG & AST_MAX_SHIFT);
+       else
+               return csub;
+}
+
+static struct chan_iax_pvt *new_iax(void)
+{
+       struct chan_iax_pvt *tmp;
+       tmp = malloc(sizeof(struct chan_iax_pvt));
+       if (tmp) {
+               memset(tmp, 0, sizeof(struct chan_iax_pvt));
+               /* On my linux system, pipe's are more than 2x as fast as socketpairs */
+               if (pipe(tmp->pipe)) {
+                       ast_log(LOG_WARNING, "Unable to create pipe: %s\n", strerror(errno));
+                       free(tmp);
+                       return NULL;
+               }
+               tmp->callno = -1;
+               tmp->peercallno = -1;
+               /* strncpy(tmp->context, context, sizeof(tmp->context)); */
+               strncpy(tmp->exten, "s", sizeof(tmp->exten));
+       }
+       return tmp;
+}
+
+static int get_timelen(struct ast_frame *f)
+{
+       int timelen=0;
+       switch(f->subclass) {
+       case AST_FORMAT_G723_1:
+               timelen = 30;
+               break;
+       case AST_FORMAT_GSM:
+               timelen = 20;
+               break;
+       case AST_FORMAT_SLINEAR:
+               timelen = f->datalen / 8;
+               break;
+       case AST_FORMAT_LPC10:
+               timelen = 22;
+               timelen += ((char *)(f->data))[7] & 0x1;
+               break;
+       default:
+               ast_log(LOG_WARNING, "Don't know how to calculate timelen on %d packets\n", f->subclass);
+       }
+       return timelen;
+}
+
+#if 0
+static struct ast_iax_frame *iaxfrdup(struct ast_iax_frame *fr)
+{
+       /* Malloc() a copy of a frame */
+       struct ast_iax_frame *new = malloc(sizeof(struct ast_iax_frame));
+       if (new) 
+               memcpy(new, fr, sizeof(struct ast_iax_frame));  
+       return new;
+}
+#endif
+
+static struct ast_iax_frame *iaxfrdup2(struct ast_iax_frame *fr, int ch)
+{
+       /* Malloc() a copy of a frame */
+       struct ast_iax_frame *new = malloc(sizeof(struct ast_iax_frame));
+       if (new) {
+               memcpy(new, fr, sizeof(struct ast_iax_frame));  
+               new->f = ast_frdup(fr->f);
+               /* Copy full header */
+               if (ch) {
+                       memcpy(new->f->data - sizeof(struct ast_iax_full_hdr),
+                                       fr->f->data - sizeof(struct ast_iax_full_hdr), 
+                                               sizeof(struct ast_iax_full_hdr));
+                       /* Grab new data pointer */
+                       new->data = new->f->data - (fr->f->data - fr->data);
+               } else {
+                       new->data = NULL;
+                       new->datalen = 0;
+               }
+       }
+       return new;
+}
+
+#define NEW_PREVENT 0
+#define NEW_ALLOW      1
+#define NEW_FORCE      2
+
+static int find_callno(short callno, 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;x<AST_IAX_MAX_CALLS;x++) {
+                       if (iaxs[x]) {
+                               /* Look for an exact match */
+                               if ((sin->sin_port == iaxs[x]->addr.sin_port) &&
+                                   (sin->sin_addr.s_addr == iaxs[x]->addr.sin_addr.s_addr) &&
+                                       ((callno == iaxs[x]->peercallno) || /* Our expected source call number is the same */
+                                        ((dcallno == x) && (iaxs[x]->peercallno = -1)) 
+                                        /* We have no expected source number, and the destination is right */
+                                        )) {
+                                       res = x;
+                                       break;
+                               }
+                       }
+               }
+       }
+       if ((res < 0) && (new >= NEW_ALLOW)) {
+               /* Create a new one */
+               start = nextcallno;
+               for (x = nextcallno + 1; iaxs[x] && (x != start); x = (x + 1) % AST_IAX_MAX_CALLS) 
+               if (x == start) {
+                       ast_log(LOG_WARNING, "Unable to accept more calls\n");
+                       return -1;
+               }
+               iaxs[x] = new_iax();
+               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;
+                       ast_sched_add(sched, ping_time * 1000, send_ping, (void *)x);
+                       ast_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)x);
+               } else
+                       ast_log(LOG_DEBUG, "Out of memory\n");
+               res = x;
+               nextcallno = x;
+       }
+       return res;
+}
+
+static int iax_send(struct chan_iax_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno);
+
+static int do_deliver(void *data)
+{
+       /* Just deliver the packet by writing it to half of the pipe. */
+       struct ast_iax_frame *fr = data;
+       unsigned int ts;
+       if (iaxs[fr->callno]) {
+               if (fr->f->frametype == AST_FRAME_IAX) {
+                       /* We have to treat some of these packets specially because
+                          they're LAG measurement packets */
+                       if (fr->f->subclass == AST_IAX_COMMAND_LAGRQ) {
+                               /* If we got a queued request, build a reply and send it */
+                               fr->f->subclass = AST_IAX_COMMAND_LAGRP;
+                               iax_send(iaxs[fr->callno], fr->f, fr->ts, -1);
+                       } else if (fr->f->subclass == AST_IAX_COMMAND_LAGRP) {
+                               /* This is a reply we've been given, actually measure the difference */
+                               ts = calc_timestamp(iaxs[fr->callno], 0);
+                               iaxs[fr->callno]->lag = ts - fr->ts;
+                       }
+               } else
+                       ast_fr_fdwrite(iaxs[fr->callno]->pipe[1], fr->f);
+       }
+       /* Free the packet */
+       ast_frfree(fr->f);
+       /* And our iax frame */
+       free(fr);
+       /* And don't run again */
+       return 0;
+}
+
+static int handle_error()
+{
+       /* 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;
+}
+
+#ifdef IAX_SIMULATOR
+static int __send_packet(struct ast_iax_frame *f)
+#else
+static int send_packet(struct ast_iax_frame *f)
+#endif
+{
+       int res;
+       if (option_debug)
+               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 < 0) {
+               ast_log(LOG_WARNING, "Call number = %d\n", f->callno);
+               return -1;
+       }
+       if (iaxs[f->callno]->error)
+               return -1;
+       res = sendto(netsocket, f->data, f->datalen, 0, &iaxs[f->callno]->addr, 
+                                       sizeof(iaxs[f->callno]->addr));
+       if (res < 0) { 
+               ast_log(LOG_WARNING, "Received error: %s\n", strerror(errno));
+               handle_error();
+       } else 
+               res = 0;
+       return res;
+}
+
+#ifdef IAX_SIMULATOR
+
+/* Average amount of delay in the connection */
+static int average_delay = 0;
+/* Permitted deviation either side of the average delay */
+static int delay_deviation = 0;
+/* Percent chance that a packet arrives O.K. */
+static int reliability = 100;
+
+static int iax_sim_calc_delay()
+{
+       int ms;
+       ms = average_delay - delay_deviation;
+       ms += ((float)(delay_deviation * 2)) * rand() / (RAND_MAX + 1.0);
+       if (ms < 0)
+               ms = 0;
+       if ((float)rand()/(RAND_MAX + 1.0) < ((float)reliability)/100)
+               return ms;
+       else
+               return -1;
+}
+
+static int d_send_packet(void *v)
+{
+       struct ast_iax_frame *f = (struct ast_iax_frame *)v;
+       if (iaxs[f->callno])
+               __send_packet(f);
+       ast_frfree(f->f);
+       free(f);
+       return 0;
+}
+
+static int send_packet(struct ast_iax_frame *f)
+{
+       struct ast_iax_frame *fn;
+       int ms;
+       ms = iax_sim_calc_delay();
+       if (ms == 0)
+               return __send_packet(f);
+       else if (ms > 0) {
+               /* Make a completely independent frame, in case the other
+                  is destroyed -- still doesn't make things like hangups
+                  arrive if the main channel is destroyed, but close enough */
+               fn = iaxfrdup2(f, 1);
+               ast_sched_add(sched, ms, d_send_packet, fn);
+       } /* else we drop the packet */
+       return 0;
+}
+
+static int iax_sim_set(int fd, int argc, char *argv[])
+{
+       if (argc != 4)
+               return RESULT_SHOWUSAGE;
+       if (!strcasecmp(argv[2], "delay")) 
+               average_delay = atoi(argv[3]);
+       else if (!strcasecmp(argv[2], "deviation"))
+               delay_deviation = atoi(argv[3]);
+       else if (!strcasecmp(argv[2], "reliability"))
+               reliability = atoi(argv[3]);
+       else 
+               return RESULT_SHOWUSAGE;
+       if (reliability > 100)
+               reliability = 100;
+       if (reliability < 0)
+               reliability = 0;
+       if (delay_deviation > average_delay)
+               delay_deviation = average_delay;
+       return RESULT_SUCCESS;
+}
+
+static char delay_usage[] = 
+"Usage: sim set delay <value>\n"
+"       Configure the IAX network simulator to generate average\n"
+"       delays equal to the specified value (in milliseconds).\n";
+
+static char deviation_usage[] = 
+"Usage: sim set deviation <value>\n"
+"       Configures the IAX network simulator's deviation value.\n"
+"       The delays generated by the simulator will always be within\n"
+"       this value of milliseconds (postive or negative) of the \n"
+"       average delay.\n";
+
+static char reliability_usage[] = 
+"Usage: sim set reliability <value>\n"
+"       Configure the probability that a packet will be delivered.\n"
+"       The value specified is a percentage from 0 to 100\n";
+
+static int iax_sim_show(int fd, int argc, char *argv[])
+{
+       if (argc != 2)
+               return RESULT_SHOWUSAGE;
+       ast_cli(fd, "Average Delay:   %d ms\n", average_delay);
+       ast_cli(fd, "Delay Deviation: %d ms\n", delay_deviation);
+       ast_cli(fd, "Reliability:     %d %\n", reliability);
+       return RESULT_SUCCESS;
+}
+
+static char sim_show_usage[] = 
+"Usage: sim show\n"
+"       Displays average delay, deviation, and reliability\n"
+"       used by the network simulator.\n";
+
+static struct ast_cli_entry delay_cli = 
+{ { "sim", "set", "delay", NULL }, iax_sim_set, "Sets simulated average delay", delay_usage };
+static struct ast_cli_entry deviation_cli = 
+{ { "sim", "set", "deviation", NULL }, iax_sim_set, "Sets simulated delay deviation", deviation_usage };
+static struct ast_cli_entry reliability_cli = 
+{ { "sim", "set", "reliability", NULL }, iax_sim_set, "Sets simulated reliability", reliability_usage };
+static struct ast_cli_entry sim_show_cli = 
+{ { "sim", "show", NULL }, iax_sim_show, "Displays simulation parameters", sim_show_usage };
+
+#endif
+
+static int attempt_transmit(void *data)
+{
+       /* Attempt to transmit the frame to the remote peer */
+       char zero = 0;
+       struct ast_iax_frame *f = data;
+       int res = 0;
+       /* Make sure this call is still active */
+       if (iaxs[f->callno]) {
+               if ((f->retries == -1) /* Already ACK'd */ ||
+                   (f->retries >= max_retries) /* Too many attempts */) {
+                               /* Record an error if we've transmitted too many times */
+                               if (f->retries >= max_retries) {
+                                       ast_log(LOG_WARNING, "Max retries exceeded to host %s (type = %d, subclass = %d, ts=%d, seqno=%d)\n", inet_ntoa(iaxs[f->callno]->addr.sin_addr), f->f->frametype, f->f->subclass, f->ts, f->seqno);
+                                       iaxs[f->callno]->error = ETIMEDOUT;
+                                       /* Send a bogus frame to wake up the waiting process */
+                                       write(iaxs[f->callno]->pipe[1], &zero, 1);
+                               }
+                               /* Don't attempt delivery, just remove it from the queue */
+                               pthread_mutex_lock(&iaxq.lock);
+                               if (f->prev) 
+                                       f->prev->next = f->next;
+                               else
+                                       iaxq.head = f->next;
+                               if (f->next)
+                                       f->next->prev = f->prev;
+                               else
+                                       iaxq.tail = f->prev;
+                               iaxq.count--;
+                               pthread_mutex_unlock(&iaxq.lock);
+               } else {
+                       /* Attempt transmission */
+                       send_packet(f);
+                       f->retries++;
+                       /* Try again later after 2 times as long */
+                       f->retrytime *= 2;
+                       if (f->retrytime > MAX_RETRY_TIME)
+                               f->retrytime = MAX_RETRY_TIME;
+                       ast_sched_add(sched, f->retrytime, attempt_transmit, f);
+                       res=0;
+               }
+       }
+       /* Do not try again */
+       return res;
+}
+
+static int iax_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) {
+                       pthread_mutex_lock(&iaxs_lock);
+                       if ((atoi(argv[3]) >= 0) && (atoi(argv[3]) < AST_IAX_MAX_CALLS)) {
+                               if (iaxs[atoi(argv[3])]) {
+                                       iaxs[atoi(argv[3])]->jitterbuffer = atoi(argv[4]);
+                                       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]));
+                       pthread_mutex_unlock(&iaxs_lock);
+               }
+       }
+       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 struct ast_cli_entry cli_set_jitter = 
+{ { "iax", "set", "jitter", NULL }, iax_set_jitter, "Sets IAX jitter buffer", jitter_usage };
+
+static unsigned int calc_rxstamp(struct chan_iax_pvt *p);
+
+static int schedule_delivery(struct ast_iax_frame *fr)
+{
+       /* XXX FIXME: I should delay delivery with a sliding jitter buffer XXX */
+       int ms,x;
+       int drops[MEMORY_SIZE];
+       int min, max=0, maxone=0,y,z, match;
+       /* ms is a measure of the "lateness" of the packet relative to the first
+          packet we received, which always has a lateness of 1.  */
+       ms = calc_rxstamp(iaxs[fr->callno]) - fr->ts;
+       
+       /* 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 < iax_dropcount + 1;z++) {
+               /* Start very pessimistic ;-) */
+               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;   
+       
+       /* 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 (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 {
+                       /* Free the packet */
+                       ast_frfree(fr->f);
+                       /* And our iax frame */
+                       free(fr);
+               }
+       } else {
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Scheduling delivery in %d ms\n", ms);
+               ast_sched_add(sched, ms, do_deliver, fr);
+       }
+       return 0;
+}
+
+static int iax_transmit(struct ast_iax_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;
+       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++;
+       pthread_mutex_unlock(&iaxq.lock);
+       /* Wake up the network thread */
+       pthread_kill(netthreadid, SIGURG);
+       return 0;
+}
+
+
+
+static int iax_digit(struct ast_channel *c, char digit)
+{
+       return send_command(c->pvt->pvt, AST_FRAME_DTMF, digit, 0, NULL, 0, -1);
+}
+
+static int create_addr(struct sockaddr_in *sin, char *peer)
+{
+       struct hostent *hp;
+       struct iax_peer *p;
+       sin->sin_family = AF_INET;
+       pthread_mutex_lock(&peerl.lock);
+       p = peerl.peers;
+       while(p) {
+               if (!strcasecmp(p->name, peer)) {
+                       sin->sin_addr = p->addr.sin_addr;
+                       sin->sin_port = p->addr.sin_port;
+                       break;
+               }
+               p = p->next;
+       }
+       pthread_mutex_unlock(&peerl.lock);
+       if (!p) {
+               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
+                       return -1;
+       } else
+               return 0;
+}
+static int iax_call(struct ast_channel *c, char *dest, int timeout)
+{
+       struct sockaddr_in sin;
+       char host[256];
+       char *rdest;
+       char *rcontext;
+       char *username;
+       char *hname;
+       char requeststr[256] = "";
+       char myrdest [5] = "s";
+       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));
+       strtok(host, ":");
+       /* If no destination extension specified, use 's' */
+       rdest = strtok(NULL, ":");
+       if (!rdest) 
+               rdest = myrdest;
+       strtok(rdest, "@");
+       rcontext = strtok(NULL, "@");
+       strtok(host, "@");
+       username = strtok(NULL, "@");
+       if (username) {
+               /* Really the second argument is the host, not the username */
+               hname = username;
+               username = host;
+       } else {
+               hname = host;
+       }
+       if (create_addr(&sin, hname)) {
+               ast_log(LOG_WARNING, "No address associated with '%s'\n", hname);
+               return -1;
+       }
+       /* Now we build our request string */
+#define MYSNPRINTF snprintf(requeststr + strlen(requeststr), sizeof(requeststr) - strlen(requeststr), 
+       MYSNPRINTF "exten=%s;", rdest);
+       if (c->callerid)
+               MYSNPRINTF "callerid=%s;", c->callerid);
+       if (c->dnid)
+               MYSNPRINTF "dnid=%s;", c->dnid);
+       if (rcontext)
+               MYSNPRINTF "context=%s;", rcontext);
+       if (username)
+               MYSNPRINTF "username=%s;", username);
+       MYSNPRINTF "formats=%d;", c->format);
+       MYSNPRINTF "version=%d;", AST_IAX_PROTO_VERSION);
+       /* Trim the trailing ";" */
+       if (strlen(requeststr))
+               requeststr[strlen(requeststr) - 1] = '\0';
+       /* Transmit the string in a "NEW" request */
+       if (option_verbose > 2)
+       ast_verbose(VERBOSE_PREFIX_3 "Calling using options '%s'\n", requeststr);
+       send_command((struct chan_iax_pvt *)c->pvt->pvt, AST_FRAME_IAX,
+               AST_IAX_COMMAND_NEW, 0, requeststr, strlen(requeststr) + 1, -1);
+       c->state = AST_STATE_RINGING;
+       return 0;
+}
+
+static void iax_destroy(int callno)
+{
+       char zero=0;
+       struct chan_iax_pvt *pvt = iaxs[callno];
+       if (pvt) {
+               if (pvt->owner) {
+                       /* If there's an owner, prod it to give up */
+                       write(pvt->pipe[1], &zero, 1);
+                       return;
+               }
+               iaxs[callno] = NULL;
+               close(pvt->pipe[0]);
+               close(pvt->pipe[1]);
+               free(pvt);
+       }
+}
+
+static int iax_hangup(struct ast_channel *c) {
+       struct chan_iax_pvt *pvt = c->pvt->pvt;
+       /* Send the hangup unless we have had a transmission error */
+       if (!pvt->error) {
+               send_command(pvt, AST_FRAME_IAX, AST_IAX_COMMAND_HANGUP, 0, NULL, 0, -1);
+               /* Wait for the network thread to transmit our command -- of course, if
+                  it doesn't, that's okay too -- the other end will find out
+                  soon enough, but it's a nicity if it can know now.  */
+               sleep(1);
+       }
+       pthread_mutex_lock(&iaxs_lock);
+       c->pvt->pvt = NULL;
+       pvt->owner = NULL;
+       pthread_mutex_lock(&usecnt_lock);
+       usecnt--;
+       if (usecnt < 0) 
+               ast_log(LOG_WARNING, "Usecnt < 0???\n");
+       pthread_mutex_unlock(&usecnt_lock);
+       ast_update_use_count();
+       if (option_verbose > 2) 
+               ast_verbose( VERBOSE_PREFIX_3 "Hungup '%s'\n", c->name);
+       iax_destroy(pvt->callno);
+       pthread_mutex_unlock(&iaxs_lock);
+       return 0;
+}
+
+static struct ast_frame *iax_read(struct ast_channel *c) 
+{
+       struct chan_iax_pvt *pvt = c->pvt->pvt;
+       struct ast_frame *f;
+       if (pvt->error) {
+               ast_log(LOG_DEBUG, "Connection closed, error: %s\n", strerror(pvt->error));
+               return NULL;
+       }
+       f = ast_fr_fdread(pvt->pipe[0]);
+       if (f) {
+               if ((f->frametype == AST_FRAME_CONTROL) &&
+                   (f->subclass == AST_CONTROL_ANSWER))
+                               c->state = AST_STATE_UP;
+       }
+       return f;
+}
+
+static int iax_answer(struct ast_channel *c)
+{
+       struct chan_iax_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 iax_write(struct ast_channel *c, struct ast_frame *f);
+
+static struct ast_channel *ast_iax_new(struct chan_iax_pvt *i, int state)
+{
+       struct ast_channel *tmp;
+       tmp = ast_channel_alloc();
+       if (tmp) {
+               snprintf(tmp->name, sizeof(tmp->name), "IAX[%s:%d]/%d", inet_ntoa(i->addr.sin_addr), ntohs(i->addr.sin_port), i->callno);
+               tmp->type = type;
+               tmp->fd = i->pipe[0];
+               /* We can support any format by default, until we get restricted */
+               tmp->format = iax_capability;
+               tmp->pvt->pvt = i;
+               tmp->pvt->send_digit = iax_digit;
+               tmp->pvt->call = iax_call;
+               tmp->pvt->hangup = iax_hangup;
+               tmp->pvt->answer = iax_answer;
+               tmp->pvt->read = iax_read;
+               tmp->pvt->write = iax_write;
+               if (strlen(i->callerid))
+                       tmp->callerid = strdup(i->callerid);
+               if (strlen(i->dnid))
+                       tmp->dnid = strdup(i->dnid);
+               strncpy(tmp->context, i->context, sizeof(tmp->context));
+               strncpy(tmp->exten, i->exten, sizeof(tmp->exten));
+               i->owner = tmp;
+               tmp->state = state;
+               pthread_mutex_lock(&usecnt_lock);
+               usecnt++;
+               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_iax_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;
+}
+
+static unsigned int calc_rxstamp(struct chan_iax_pvt *p)
+{
+       /* Returns where in "receive time" we are */
+       struct timeval tv;
+       unsigned int ms;
+       if (!p->rxcore.tv_sec && !p->rxcore.tv_usec)
+               gettimeofday(&p->rxcore, NULL);
+       gettimeofday(&tv, NULL);
+       ms = (tv.tv_sec - p->offset.tv_sec) * 1000 + (tv.tv_usec - p->offset.tv_usec) / 1000;
+       return ms;
+}
+static int iax_send(struct chan_iax_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno)
+{
+       /* Queue a packet for delivery on a given private structure.  Use "ts" for
+          timestamp, or calculate if ts is 0 */
+       struct ast_iax_full_hdr *fh;
+       struct ast_iax_mini_hdr *mh;
+       struct ast_iax_frame *fr;
+       int res;
+       unsigned int lastsent;
+       /* Allocate an ast_iax_frame */
+       fr = malloc(sizeof(struct ast_iax_frame));
+       if (!fr) {
+               ast_log(LOG_WARNING, "Out of memory\n");
+               return -1;
+       }
+       if (!pvt) {
+               ast_log(LOG_WARNING, "No private structure for packet (%d)?\n", fr->callno);
+               free(fr);
+               return -1;
+       }
+       /* Isolate our frame for transmission */
+       fr->f = ast_frdup(f);
+       if (!fr->f) {
+               ast_log(LOG_WARNING, "Out of memory\n");
+               free(fr);
+               return -1;
+       }
+       if (fr->f->offset < sizeof(struct ast_iax_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");
+               return -1;
+       }
+       fr->callno = pvt->callno;
+       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->seqno = seqno;
+               else
+                       fr->seqno = pvt->oseqno++;
+               fh = (struct ast_iax_full_hdr *)(fr->f->data - sizeof(struct ast_iax_full_hdr));
+               fh->callno = htons(fr->callno | AST_FLAG_FULL);
+               fh->ts = htonl(fr->ts);
+               fh->seqno = htons(fr->seqno);
+               fh->type = fr->f->frametype & 0xFF;
+               fh->csub = compress_subclass(fr->f->subclass);
+#if 0
+               fh->subclasshigh = (fr->f->subclass & 0xFF0000) >> 16;
+               fh->subclasslow = htons(fr->f->subclass & 0xFFFF);
+#endif
+               fh->dcallno = htons(pvt->peercallno);
+               fr->datalen = fr->f->datalen + sizeof(struct ast_iax_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_IAX_COMMAND_ACK))
+                       fr->retries = -1;
+               if (f->frametype == AST_FRAME_VOICE) {
+                       pvt->svoiceformat = f->subclass;
+               }
+               res = iax_transmit(fr);
+       } else {
+               /* Mini-frames have no sequence number */
+               fr->seqno = -1;
+               /* Mini frame will do */
+               mh = (struct ast_iax_mini_hdr *)(fr->f->data - sizeof(struct ast_iax_mini_hdr));
+               mh->callno = htons(fr->callno);
+               mh->ts = htons(fr->ts & 0xFFFF);
+               fr->datalen = fr->f->datalen + sizeof(struct ast_iax_mini_hdr);
+               fr->data = mh;
+               fr->retries = -1;
+               res = iax_transmit(fr);
+       }
+       return res;
+}
+
+
+
+static int iax_show_users(int fd, int argc, char *argv[])
+{
+#define FORMAT "%-15.15s  %-15.15s  %-15.15s  %-15.15s  %-5.5s\n"
+       struct iax_user *user;
+       if (argc != 3) 
+               return RESULT_SHOWUSAGE;
+       pthread_mutex_lock(&userl.lock);
+       ast_cli(fd, FORMAT, "Username", "Secret", "Authen", "Def.Context", "A/C");
+       for(user=userl.users;user;user=user->next) {
+               ast_cli(fd, FORMAT, user->name, user->secret, user->methods, 
+                               user->contexts ? user->contexts->context : context,
+                               user->ha ? "Yes" : "No");
+       }
+       pthread_mutex_unlock(&userl.lock);
+       return RESULT_SUCCESS;
+#undef FORMAT
+}
+
+static int iax_show_peers(int fd, int argc, char *argv[])
+{
+#define FORMAT2 "%-15.15s  %-15.15s  %-15.15s  %-15.15s  %s\n"
+#define FORMAT "%-15.15s  %-15.15s  %-15.15s  %-15.15s  %d\n"
+       struct iax_peer *peer;
+       if (argc != 3)
+               return RESULT_SHOWUSAGE;
+       pthread_mutex_lock(&peerl.lock);
+       ast_cli(fd, FORMAT2, "Name", "Username", "Host", "Mask", "Port");
+       for (peer = peerl.peers;peer;peer = peer->next) {
+               char nm[20];
+               strncpy(nm, inet_ntoa(peer->mask), sizeof(nm));
+               ast_cli(fd, FORMAT, peer->name, 
+                                       peer->username ? peer->username : "(Any)",
+                                       peer->addr.sin_addr.s_addr ? inet_ntoa(peer->addr.sin_addr) : "(Any)",
+                                       nm,
+                                       ntohs(peer->addr.sin_port));
+       }
+       pthread_mutex_unlock(&peerl.lock);
+       return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static int iax_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;
+       if (argc != 3)
+               return RESULT_SHOWUSAGE;
+       pthread_mutex_lock(&iaxs_lock);
+       ast_cli(fd, FORMAT2, "Peer", "Username", "ID (Lo/Rem)", "Seq (Tx/Rx)", "Lag", "Jitter", "Format");
+       for (x=0;x<AST_IAX_MAX_CALLS;x++)
+               if (iaxs[x]) 
+                       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);
+       pthread_mutex_unlock(&iaxs_lock);
+       return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static char show_users_usage[] = 
+"Usage: iax show users\n"
+"       Lists all users known to the IAX (Inter-Asterisk eXchange) subsystem.\n";
+
+static char show_channels_usage[] = 
+"Usage: iax show channels\n"
+"       Lists all currently active IAX channels.\n";
+
+static char show_peers_usage[] = 
+"Usage: iax show peers\n"
+"       Lists all known IAX peers.\n";
+
+static struct ast_cli_entry  cli_show_users = 
+       { { "iax", "show", "users", NULL }, iax_show_users, "Show defined IAX users", show_users_usage };
+static struct ast_cli_entry  cli_show_channels =
+       { { "iax", "show", "channels", NULL }, iax_show_channels, "Show active IAX channels", show_channels_usage };
+static struct ast_cli_entry  cli_show_peers =
+       { { "iax", "show", "peers", NULL }, iax_show_peers, "Show defined IAX peers", show_peers_usage };
+
+static int iax_write(struct ast_channel *c, struct ast_frame *f)
+{
+       struct chan_iax_pvt *i = c->pvt->pvt;
+       /* If there's an outstanding error, return failure now */
+       if (i->error) {
+               ast_log(LOG_DEBUG, "Write error: %s\n", strerror(errno));
+               return -1;
+       }
+       /* Don't waste bandwidth sending null frames */
+       if (f->frametype == AST_FRAME_NULL)
+               return 0;
+       /* Simple, just queue for transmission */
+       return iax_send(i, f, 0, -1);
+}
+
+static int send_command(struct chan_iax_pvt *i, char type, int command, unsigned int ts, char *data, int datalen, int seqno)
+{
+       struct ast_frame f;
+       f.frametype = type;
+       f.subclass = command;
+       f.datalen = datalen;
+       f.timelen = 0;
+       f.mallocd = 0;
+       f.offset = 0;
+       f.src = __FUNCTION__;
+       f.data = data;
+       return iax_send(i, &f, ts, seqno);
+}
+
+static int apply_context(struct iax_context *con, char *context)
+{
+       while(con) {
+               if (!strcmp(con->context, context))
+                       return -1;
+               con = con->next;
+       }
+       return 0;
+}
+
+static int apply_ha(struct iax_ha *ha, struct sockaddr_in *sin)
+{
+       /* Start optimistic */
+       int res = IAX_SENSE_ALLOW;
+       while(ha) {
+               /* For each rule, if this address and the netmask = the net address
+                  apply the current rule */
+               if ((sin->sin_addr.s_addr & ha->netmask.s_addr) == (ha->netaddr.s_addr))
+                       res = ha->sense;
+               ha = ha->next;
+       }
+       return res;
+}
+
+static int check_access(int callno, struct sockaddr_in *sin, char *orequest, int requestl)
+{
+       /* Start pessimistic */
+       int res = -1;
+       int version = 1;
+       char *var, *value;
+       struct iax_user *user;
+       char request[256];
+       strncpy(request, orequest, sizeof(request));
+       if (!iaxs[callno])
+               return res;
+       var = strtok(request, ";");
+       while(var) {
+               value = strchr(var, '=');
+               if (value) { 
+                       *value='\0';
+                       value++;
+                       if (!strcmp(var, "exten")) 
+                               strncpy(iaxs[callno]->exten, value, sizeof(iaxs[callno]->exten));
+                       else if (!strcmp(var, "callerid"))
+                               strncpy(iaxs[callno]->callerid, value, sizeof(iaxs[callno]->callerid));
+                       else if (!strcmp(var, "dnid"))
+                               strncpy(iaxs[callno]->dnid, value, sizeof(iaxs[callno]->dnid));
+                       else if (!strcmp(var, "context"))
+                               strncpy(iaxs[callno]->context, value, sizeof(iaxs[callno]->context));
+                       else if (!strcmp(var, "username"))
+                               strncpy(iaxs[callno]->username, value, sizeof(iaxs[callno]->username));
+                       else if (!strcmp(var, "formats"))
+                               iaxs[callno]->peerformats = atoi(value);
+                       else if (!strcmp(var, "version"))
+                               version = atoi(value);
+                       else 
+                               ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
+               }
+               var = strtok(NULL, ";");
+       }
+       if (version > AST_IAX_PROTO_VERSION) {
+               ast_log(LOG_WARNING, "Peer '%s' has too new a protocol version (%d) for me\n", 
+                       inet_ntoa(sin->sin_addr), version);
+               return res;
+       }
+       pthread_mutex_lock(&userl.lock);
+       /* Search the userlist for a compatible entry, and fill in the rest */
+       user = userl.users;
+       while(user) {
+               if ((!strlen(iaxs[callno]->username) ||                         /* No username specified */
+                       !strcmp(iaxs[callno]->username, user->name))    /* Or this username specified */
+                       && (apply_ha(user->ha, sin) == IAX_SENSE_ALLOW) /* Access is permitted from this IP */
+                       && (!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));
+                       /* And use the default context */
+                       if (!strlen(iaxs[callno]->context)) {
+                               if (user->contexts)
+                                       strncpy(iaxs[callno]->context, user->contexts->context, sizeof(iaxs[callno]->context));
+                               else
+                                       strncpy(iaxs[callno]->context, context, sizeof(iaxs[callno]->context));
+                       }
+                       /* Copy the secret */
+                       strncpy(iaxs[callno]->secret, user->secret, sizeof(iaxs[callno]->secret));
+                       /* And the permitted authentication methods */
+                       strncpy(iaxs[callno]->methods, user->methods, sizeof(iaxs[callno]->methods));
+                       res = 0;
+                       break;
+               }
+               user = user->next;      
+       }
+       pthread_mutex_unlock(&userl.lock);
+       return res;
+}
+
+static int raw_hangup(struct sockaddr_in *sin, short src, short dst)
+{
+       struct ast_iax_full_hdr fh;
+       fh.callno = htons(src | AST_FLAG_FULL);
+       fh.dcallno = htons(dst);
+       fh.ts = 0;
+       fh.seqno = 0;
+       fh.type = AST_FRAME_IAX;
+       fh.csub = compress_subclass(AST_IAX_COMMAND_INVAL);
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Raw Hangup\n");
+       return sendto(netsocket, &fh, sizeof(fh), 0, sin, sizeof(*sin));
+}
+
+static int authenticate_request(struct chan_iax_pvt *p)
+{
+       char requeststr[256] = "";
+       MYSNPRINTF "methods=%s;", p->methods);
+       if (strstr(p->methods, "md5")) {
+               /* Build the challenge */
+               srand(time(NULL));
+               snprintf(p->challenge, sizeof(p->challenge), "%d", rand());
+               MYSNPRINTF "challenge=%s;", p->challenge);
+       }
+       MYSNPRINTF "username=%s;", p->username);
+       if (strlen(requeststr))
+               requeststr[strlen(requeststr) - 1] = '\0';
+       return send_command(p, AST_FRAME_IAX, AST_IAX_COMMAND_AUTHREQ, 0, requeststr, strlen(requeststr) + 1, -1);
+}
+
+static int authenticate_verify(struct chan_iax_pvt *p, char *orequest)
+{
+       char requeststr[256] = "";
+       char *var, *value, request[256];
+       char md5secret[256] = "";
+       char secret[256] = "";
+       int res = -1; 
+       int x;
+       
+       if (!(p->state & IAX_STATE_AUTHENTICATED))
+               return res;
+       strncpy(request, orequest, sizeof(request));
+       var = strtok(request, ";");
+       while(var) {
+               value = strchr(var, '=');
+               if (value) { 
+                       *value='\0';
+                       value++;
+                       if (!strcmp(var, "secret")) 
+                               strncpy(secret, value, sizeof(secret));
+                       else if (!strcmp(var, "md5secret"))
+                               strncpy(md5secret, value, sizeof(md5secret));
+                       else 
+                               ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
+               }
+               var = strtok(NULL, ";");
+       }
+       if (strstr(p->methods, "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++)
+                       MYSNPRINTF "%2.2x", digest[x]);
+               if (!strcasecmp(requeststr, md5secret))
+                       res = 0;
+       } else if (strstr(p->methods, "plaintext")) {
+               if (!strcmp(secret, p->secret))
+                       res = 0;
+       }
+       return res;
+}
+
+static int authenticate_reply(struct chan_iax_pvt *p, struct sockaddr_in *sin, char *orequest)
+{
+       struct iax_peer *peer;
+       /* Start pessimistic */
+       int res = -1;
+       char request[256];
+       char methods[80] = "";
+       char requeststr[256] = "";
+       char *var, *value;
+       int x;
+       strncpy(request, orequest, sizeof(request));
+       var = strtok(request, ";");
+       while(var) {
+               value = strchr(var, '=');
+               if (value) { 
+                       *value='\0';
+                       value++;
+                       if (!strcmp(var, "username")) 
+                               strncpy(p->username, value, sizeof(p->username));
+                       else if (!strcmp(var, "challenge"))
+                               strncpy(p->challenge, value, sizeof(p->challenge));
+                       else if (!strcmp(var, "methods"))
+                               strncpy(methods, value, sizeof(methods));
+                       else 
+                               ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
+               }
+               var = strtok(NULL, ";");
+       }
+       pthread_mutex_lock(&peerl.lock);
+       peer = peerl.peers;
+       while(peer) {
+               if ((!strlen(p->peer) || !strcmp(p->peer, peer->name)) 
+                                                               /* No peer specified at our end, or this is the peer */
+                        && (!strlen(peer->username) || (!strcmp(peer->username, p->username)))
+                                                               /* No username specified in peer rule, or this is the right username */
+                        && (!peer->addr.sin_addr.s_addr || ((sin->sin_addr.s_addr & peer->mask.s_addr) == (peer->addr.sin_addr.s_addr & peer->mask.s_addr)))
+                                                               /* No specified host, or this is our host */
+                       ) {
+                       /* We have a match, authenticate it. */
+                       res = 0;
+                       if (strstr(methods, "md5")) {
+                               struct MD5Context md5;
+                               unsigned char digest[16];
+                               MD5Init(&md5);
+                               MD5Update(&md5, p->challenge, strlen(p->challenge));
+                               MD5Update(&md5, peer->secret, strlen(peer->secret));
+                               MD5Final(digest, &md5);
+                               /* If they support md5, authenticate with it.  */
+                               MYSNPRINTF "md5secret=");
+                               for (x=0;x<16;x++)
+                                       MYSNPRINTF "%2.2x", digest[x]);
+                               MYSNPRINTF ";");
+                       } else if (strstr(methods, "plaintext")) {
+                               MYSNPRINTF "secret=%s;", peer->secret);
+                       } else 
+                               res = -1;
+                       if (strlen(requeststr))
+                               requeststr[strlen(requeststr)-1] = '\0';
+                       if (!res)
+                               res = send_command(p, AST_FRAME_IAX, AST_IAX_COMMAND_AUTHREP, 0, requeststr, strlen(requeststr) + 1, -1);
+                       break;  
+               }
+               peer = peer->next;
+       }
+       pthread_mutex_unlock(&peerl.lock);
+       return res;
+}
+
+static int socket_read(int *id, int fd, short events, void *cbdata)
+{
+       struct sockaddr_in sin;
+       int res;
+       int new = NEW_PREVENT;
+       char buf[4096];
+       char src[80];
+       int len = sizeof(sin);
+       int dcallno = -1;
+       struct ast_iax_full_hdr *fh = (struct ast_iax_full_hdr *)buf;
+       struct ast_iax_mini_hdr *mh = (struct ast_iax_mini_hdr *)buf;
+       struct ast_iax_frame fr, *cur;
+       struct ast_frame f;
+       struct ast_channel *c;
+       res = recvfrom(netsocket, buf, sizeof(buf), 0, &sin, &len);
+       if (res < 0) {
+               ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
+               handle_error();
+               return 1;
+       }
+       if (res < sizeof(struct ast_iax_mini_hdr)) {
+               ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, sizeof(struct ast_iax_mini_hdr));
+               return 1;
+       }
+       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_IAX_COMMAND_NEW))
+                       new = NEW_ALLOW;
+       }
+       pthread_mutex_lock(&iaxs_lock);
+       fr.callno = find_callno(ntohs(mh->callno) & ~AST_FLAG_FULL, dcallno, &sin, new);
+       if ((fr.callno < 0) || !iaxs[fr.callno]) {
+               /* A call arrived for a non-existant destination.  Unless it's an "inval"
+                  frame, reply with an inval */
+               if (ntohs(mh->callno) & AST_FLAG_FULL) {
+                       /* We can only raw hangup control frames */
+                       if ((f.subclass != AST_IAX_COMMAND_INVAL) || (f.frametype != AST_FRAME_IAX))
+                               raw_hangup(&sin, ntohs(fh->dcallno), ntohs(mh->callno));
+               }
+               pthread_mutex_unlock(&iaxs_lock);
+               return 1;
+       }
+       iaxs[fr.callno]->peercallno = ntohs(mh->callno) & ~AST_FLAG_FULL;
+       if (ntohs(mh->callno) & AST_FLAG_FULL) {
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Received packet %d, (%d, %d)\n", ntohs(fh->seqno), f.frametype, f.subclass);
+               /* Check if it's out of order (and not an ACK or INVAL) */
+               fr.seqno = ntohs(fh->seqno);
+               if (iaxs[fr.callno]->iseqno != fr.seqno) {
+                       if (
+                        ((f.subclass != AST_IAX_COMMAND_ACK) && (f.subclass != AST_IAX_COMMAND_INVAL)) ||
+                        (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.seqno, f.frametype, f.subclass);
+                               if (iaxs[fr.callno]->iseqno > fr.seqno) {
+                                       /* 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_IAX_COMMAND_ACK) && (f.subclass != AST_IAX_COMMAND_INVAL))) {
+                                               if (option_debug)
+                                                       ast_log(LOG_DEBUG, "Acking anyway\n");
+                                               send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.seqno);
+                                       }
+                               }
+                               pthread_mutex_unlock(&iaxs_lock);
+                               return 1;
+                       }
+               } else {
+                       /* Increment unless it's an ACK */
+                       if ((f.subclass != AST_IAX_COMMAND_ACK) ||
+                           (f.frametype != AST_FRAME_IAX))
+                               iaxs[fr.callno]->iseqno++;
+               }
+               /* A full frame */
+               if (res < sizeof(struct ast_iax_full_hdr)) {
+                       ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, sizeof(struct ast_iax_full_hdr));
+                       pthread_mutex_unlock(&iaxs_lock);
+                       return 1;
+               }
+               f.datalen = res - sizeof(struct ast_iax_full_hdr);
+               if (f.datalen)
+                       f.data = buf + sizeof(struct ast_iax_full_hdr);
+               else
+                       f.data = NULL;
+               fr.ts = ntohl(fh->ts);
+               /* Unless this is an ACK or INVAL frame, ack it */
+               if ((f.frametype != AST_FRAME_IAX) || 
+                        ((f.subclass != AST_IAX_COMMAND_ACK) && (f.subclass != AST_IAX_COMMAND_INVAL))) 
+                       send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.seqno);
+               if (f.frametype == AST_FRAME_VOICE)
+                       iaxs[fr.callno]->voiceformat = f.subclass;
+               if (f.frametype == AST_FRAME_IAX) {
+                       /* Handle the IAX pseudo frame itself */
+                       if (option_debug)
+                               ast_log(LOG_DEBUG, "IAX subclass %d received\n", f.subclass);
+                       switch(f.subclass) {
+                       case AST_IAX_COMMAND_ACK:
+                               /* Ack the packet with the given timestamp */
+                               pthread_mutex_lock(&iaxq.lock);
+                               for (cur = iaxq.head; cur ; cur = cur->next) {
+                                       /* If it's our call, and our timestamp, mark -1 retries */
+                                       if ((fr.callno == cur->callno) && (fr.seqno == cur->seqno))
+                                               cur->retries = -1;
+                               }
+                               pthread_mutex_unlock(&iaxq.lock);
+                               break;
+                       case AST_IAX_COMMAND_NEW:
+                               ((char *)f.data)[f.datalen] = '\0';
+                               if (check_access(fr.callno, &sin, f.data, f.datalen)) {
+                                       /* They're not allowed on */
+                                       send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No authority found", strlen("No authority found"), -1);
+                                       ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s'\n", inet_ntoa(sin.sin_addr), f.data);
+                                       /* XXX Not guaranteed to work, but probably does XXX */
+                                       pthread_mutex_lock(&iaxq.lock);
+                                       send_packet(iaxq.tail);
+                                       pthread_mutex_unlock(&iaxq.lock);
+                                       iax_destroy(fr.callno);
+                                       break;
+                               }
+                               if (!strlen(iaxs[fr.callno]->secret)) {
+                                       /* No authentication required, let them in */
+                                       send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACCEPT, 0, NULL, 0, -1);
+                                       iaxs[fr.callno]->state |= IAX_STATE_STARTED;
+                                       if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING)))
+                                               iax_destroy(fr.callno);
+                                       else
+                                               c->format = iaxs[fr.callno]->peerformats;
+                                       break;
+                               }
+                               authenticate_request(iaxs[fr.callno]);
+                               iaxs[fr.callno]->state |= IAX_STATE_AUTHENTICATED;
+                               break;
+                       case AST_IAX_COMMAND_HANGUP:
+#if 0
+                               iaxs[fr.callno]->error = ENOTCONN;
+#endif
+                               iax_destroy(fr.callno);
+                               break;
+                       case AST_IAX_COMMAND_REJECT:
+                               ((char *)f.data)[f.datalen] = '\0';
+                               ast_log(LOG_WARNING, "Call rejected by %s: %s\n", inet_ntoa(iaxs[fr.callno]->addr.sin_addr), f.data);
+                               iaxs[fr.callno]->error = EPERM;
+                               iax_destroy(fr.callno);
+                               break;
+                       case AST_IAX_COMMAND_ACCEPT:
+                               if (option_verbose > 2)
+                                       ast_verbose(VERBOSE_PREFIX_3 "Call accepted by %s\n", inet_ntoa(iaxs[fr.callno]->addr.sin_addr));
+                               iaxs[fr.callno]->state |= IAX_STATE_STARTED;
+                               break;
+                       case AST_IAX_COMMAND_PING:
+                               /* Send back a pong packet with the original timestamp */
+                               send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_PONG, fr.ts, NULL, 0, -1);
+                               break;
+                       case AST_IAX_COMMAND_PONG:
+                               iaxs[fr.callno]->pingtime =  calc_timestamp(iaxs[fr.callno], 0) - fr.ts;
+                               break;
+                       case AST_IAX_COMMAND_LAGRQ:
+                       case AST_IAX_COMMAND_LAGRP:
+                               /* A little strange -- We have to actually go through the motions of
+                                  delivering the packet.  In the very last step, it will be properly
+                                  handled by do_deliver */
+                               snprintf(src, sizeof(src), "LAGRQ-IAX/%s/%d", inet_ntoa(sin.sin_addr),fr.callno);
+                               f.src = src;
+                               f.mallocd = 0;
+                               f.offset = 0;
+                               fr.f = &f;
+                               f.timelen = 0;
+                               schedule_delivery(iaxfrdup2(&fr, 0));
+                               break;
+                       case AST_IAX_COMMAND_AUTHREQ:
+                               ((char *)f.data)[f.datalen] = '\0';
+                               if (authenticate_reply(iaxs[fr.callno], &iaxs[fr.callno]->addr, (char *)f.data)) {
+                                       ast_log(LOG_WARNING, 
+                                               "I don't know how to authenticate %s to %s\n", 
+                                               f.data, inet_ntoa(iaxs[fr.callno]->addr.sin_addr));
+                                       iax_destroy(fr.callno);
+                               }
+                               break;
+                       case AST_IAX_COMMAND_AUTHREP:
+                               ((char *)f.data)[f.datalen] = '\0';
+                               if (authenticate_verify(iaxs[fr.callno], (char *)f.data)) {
+                                       ast_log(LOG_NOTICE, "Host %s failed to authenticate as %s\n", inet_ntoa(iaxs[fr.callno]->addr.sin_addr), iaxs[fr.callno]->username);
+                                       send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No authority found", strlen("No authority found"), -1);
+                                       /* XXX Not guaranteed to work, but probably does XXX */
+                                       pthread_mutex_lock(&iaxq.lock);
+                                       send_packet(iaxq.tail);
+                                       pthread_mutex_unlock(&iaxq.lock);
+                                       iax_destroy(fr.callno);
+                                       break;
+                               }
+                               /* Authentication is fine, go ahead */
+                               send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACCEPT, 0, NULL, 0, -1);
+                               iaxs[fr.callno]->state |= IAX_STATE_STARTED;
+                               if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING)))
+                                       iax_destroy(fr.callno);
+                               else
+                                       c->format = iaxs[fr.callno]->peerformats;
+                               break;
+                       case AST_IAX_COMMAND_INVAL:
+                               iaxs[fr.callno]->error = ENOTCONN;
+                               iax_destroy(fr.callno);
+                               if (option_debug)
+                                       ast_log(LOG_DEBUG, "Destroying call %d\n", fr.callno);
+                               break;
+                       default:
+                               ast_log(LOG_DEBUG, "Unknown IAX command %d on %d/%d\n", f.subclass, fr.callno, iaxs[fr.callno]->peercallno);
+                       }
+                       /* Don't actually pass these frames along */
+                       pthread_mutex_unlock(&iaxs_lock);
+                       return 1;
+               }
+       } 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 ");
+                       pthread_mutex_unlock(&iaxs_lock);
+                       return 1;
+               }
+               f.datalen = res - sizeof(struct ast_iax_mini_hdr);
+               if (f.datalen < 0) {
+                       ast_log(LOG_WARNING, "Datalen < 0?\n");
+                       pthread_mutex_unlock(&iaxs_lock);
+                       return 1;
+               }
+               if (f.datalen)
+                       f.data = buf + sizeof(struct ast_iax_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)) {
+               pthread_mutex_unlock(&iaxs_lock);
+               return 1;
+       }
+       /* Common things */
+       snprintf(src, sizeof(src), "IAX/%s/%d", inet_ntoa(sin.sin_addr),fr.callno);
+       f.src = src;
+       f.mallocd = 0;
+       f.offset = 0;
+       fr.f = &f;
+       if (f.datalen && (f.frametype == AST_FRAME_VOICE)) 
+               f.timelen = get_timelen(&f);
+       else
+               f.timelen = 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 {
+               ast_log(LOG_DEBUG, "Received out of order packet... (type=%d, subclass %d, ts = %d, last = %d)\n", f.frametype, f.subclass, fr.ts, iaxs[fr.callno]->last);
+               fr.outoforder = -1;
+       }
+       schedule_delivery(iaxfrdup2(&fr, 0));
+       /* Always run again */
+       pthread_mutex_unlock(&iaxs_lock);
+       return 1;
+}
+
+static void free_ha(struct iax_ha *ha)
+{
+       struct iax_ha *hal;
+       while(ha) {
+               hal = ha;
+               ha = ha->next;
+               free(hal);
+       }
+}
+
+static void free_context(struct iax_context *con)
+{
+       struct iax_context *conl;
+       while(con) {
+               conl = con;
+               con = con->next;
+               free(conl);
+       }
+}
+
+static struct ast_channel *iax_request(char *type, int format, void *data)
+{
+       int callno;
+       struct sockaddr_in sin;
+       char s[256];
+       char *st;
+       struct ast_channel *c;
+       strncpy(s, (char *)data, sizeof(s));
+       strtok(s, ":");
+       strtok(s, "@");
+       st = strtok(NULL, "@");
+       if (!st)
+               st = s;
+       /* Populate our address from the given */
+       if (create_addr(&sin, st)) {
+               ast_log(LOG_WARNING, "Unable to assign address\n");
+               return NULL;
+       }
+       pthread_mutex_lock(&iaxs_lock);
+       callno = find_callno(-1, -1, &sin, NEW_FORCE);
+       if (callno < 0) {
+               ast_log(LOG_WARNING, "Unable to create call\n");
+               return NULL;
+       }
+       c = ast_iax_new(iaxs[callno], AST_STATE_DOWN);
+       if (c) {
+               /* Choose a format we can live with */
+               if (c->format & format)
+                       c->format &= format;
+               else 
+                       c->format = ast_translator_best_choice(format, c->format);
+       }
+       pthread_mutex_unlock(&iaxs_lock);
+       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_iax_frame *f, *freeme;
+       /* Establish I/O callback for socket read */
+       ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
+       pthread_mutex_lock(&iaxs_lock);
+       for(;;) {
+               /* Go through the queue, sending messages which have not yet been
+                  sent, and scheduling retransmissions if appropriate */
+               pthread_mutex_lock(&iaxq.lock);
+               f = iaxq.head;
+               while(f) {
+                       freeme = NULL;
+                       if (!f->sentyet) {
+                               f->sentyet++;
+                               /* Send a copy immediately */
+                               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);
+                                       /* Free the iax frame */
+                                       freeme = f;
+                               } else {
+                                       /* We need reliable delivery.  Schedule a retransmission */
+                                       f->retries++;
+                                       ast_sched_add(sched, f->retrytime, attempt_transmit, f);
+                               }
+                       }
+                       f = f->next;
+                       if (freeme)
+                               free(freeme);
+               }
+               pthread_mutex_unlock(&iaxq.lock);
+               pthread_mutex_unlock(&iaxs_lock);
+               res = ast_sched_wait(sched);
+               res = ast_io_wait(io, res);
+               pthread_mutex_lock(&iaxs_lock);
+               if (res >= 0) {
+                       ast_sched_runq(sched);
+               }
+       }
+}
+
+static int start_network_thread()
+{
+       return pthread_create(&netthreadid, NULL, network_thread, NULL);
+}
+
+static struct iax_context *build_context(char *context)
+{
+       struct iax_context *con = malloc(sizeof(struct iax_context));
+       if (con) {
+               strncpy(con->context, context, sizeof(con->context));
+               con->next = NULL;
+       }
+       return con;
+}
+
+static struct iax_ha *build_ha(char *sense, char *stuff)
+{
+       struct iax_ha *ha = malloc(sizeof(struct iax_ha));
+       char *nm;
+       if (ha) {
+               strtok(stuff, "/");
+               nm = strtok(NULL, "/");
+               if (!nm)
+                       nm = "255.255.255.255";
+               if (!inet_aton(stuff, &ha->netaddr)) {
+                       ast_log(LOG_WARNING, "%s not a valid IP\n", stuff);
+                       free(ha);
+                       return NULL;
+               }
+               if (!inet_aton(nm, &ha->netmask)) {
+                       ast_log(LOG_WARNING, "%s not a valid netmask\n", nm);
+                       free(ha);
+                       return NULL;
+               }
+               ha->netaddr.s_addr &= ha->netmask.s_addr;
+               if (!strcasecmp(sense, "a")) {
+                       ha->sense = IAX_SENSE_ALLOW;
+               } else {
+                       ha->sense = IAX_SENSE_DENY;
+               }
+               ha->next = NULL;
+       }
+       return ha;
+}
+
+static struct iax_peer *build_peer(char *name, struct ast_variable *v)
+{
+       struct iax_peer *peer;
+       int maskfound=0;
+       struct hostent *hp;
+       peer = malloc(sizeof(struct iax_peer));
+       if (peer) {
+               memset(peer, 0, sizeof(struct iax_peer));
+               strncpy(peer->name, name, sizeof(peer->name));
+               peer->addr.sin_port = htons(AST_DEFAULT_IAX_PORTNO);
+               while(v) {
+                       if (!strcasecmp(v->name, "secret")) 
+                               strncpy(peer->secret, v->value, sizeof(peer->secret));
+                       else if (!strcasecmp(v->name, "host")) {
+                               hp = gethostbyname(v->value);
+                               if (hp) {
+                                       memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
+                               } else {
+                                       ast_log(LOG_WARNING, "Unable to lookup '%s'\n", v->value);
+                                       free(peer);
+                                       return NULL;
+                               }
+                               if (!maskfound)
+                                       inet_aton("255.255.255.255", &peer->mask);
+                       }
+                       else if (!strcasecmp(v->name, "mask")) {
+                               maskfound++;
+                               inet_aton(v->value, &peer->mask);
+                       } else if (!strcasecmp(v->name, "port"))
+                               peer->addr.sin_port = htons(atoi(v->value));
+                       else if (!strcasecmp(v->name, "username"))
+                               strncpy(peer->username, v->value, sizeof(peer->username));
+                       v=v->next;
+               }
+       }
+       return peer;
+}
+
+static struct iax_user *build_user(char *name, struct ast_variable *v)
+{
+       struct iax_user *user;
+       struct iax_context *con, *conl = NULL;
+       struct iax_ha *ha, *hal = NULL;
+       user = (struct iax_user *)malloc(sizeof(struct iax_user));
+       if (user) {
+               memset(user, 0, sizeof(struct iax_user));
+               strncpy(user->name, name, sizeof(user->name));
+               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, "allow") ||
+                                          !strcasecmp(v->name, "deny")) {
+                               ha = build_ha(v->name, v->value);
+                               if (ha) {
+                                       if (hal)
+                                               hal->next = ha;
+                                       else
+                                               user->ha = ha;
+                                       hal = ha;
+                               }
+                       } else if (!strcasecmp(v->name, "auth")) {
+                               strncpy(user->methods, v->value, sizeof(user->methods));
+                       } else if (!strcasecmp(v->name, "secret")) {
+                               strncpy(user->secret, v->value, sizeof(user->secret));
+                       }
+                       v = v->next;
+               }
+       }
+       return user;
+}
+
+int load_module()
+{
+       int res = 0;
+       struct ast_config *cfg;
+       struct ast_variable *v;
+       struct iax_user *user;
+       struct iax_peer *peer;
+       char *cat;
+       char *utype;
+       int format;
+       
+       struct sockaddr_in sin;
+       
+       sin.sin_family = AF_INET;
+       sin.sin_port = ntohs(AST_DEFAULT_IAX_PORTNO);
+       sin.sin_addr.s_addr = INADDR_ANY;
+       
+       io = io_context_create();
+       sched = sched_context_create();
+       
+       if (!io || !sched) {
+               ast_log(LOG_ERROR, "Out of memory\n");
+               return -1;
+       }
+
+       pthread_mutex_init(&iaxq.lock, NULL);
+       pthread_mutex_init(&userl.lock, NULL);
+
+       ast_cli_register(&cli_show_users);
+       ast_cli_register(&cli_show_channels);
+       ast_cli_register(&cli_show_peers);
+       ast_cli_register(&cli_set_jitter);
+#ifdef IAX_SIMULATOR
+       ast_cli_register(&delay_cli);
+       ast_cli_register(&deviation_cli);
+       ast_cli_register(&reliability_cli);
+       ast_cli_register(&sim_show_cli);
+#endif
+       cfg = ast_load(config);
+       
+       if (!cfg) {
+               ast_log(LOG_ERROR, "Unable to load config %s\n", config);
+               return -1;
+       }
+       v = ast_variable_browse(cfg, "general");
+       while(v) {
+               if (!strcasecmp(v->name, "port")) 
+                       sin.sin_port = ntohs(atoi(v->value));
+               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")) 
+                       iax_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")) {
+                               iax_capability = IAX_CAPABILITY_LOWBANDWIDTH;
+                       } else if (!strcasecmp(v->value, "medium")) {
+                               iax_capability = IAX_CAPABILITY_MEDBANDWIDTH;
+                       } else if (!strcasecmp(v->value, "high")) {
+                               iax_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
+                               iax_capability |= format;
+               } else if (!strcasecmp(v->name, "disallow")) {
+                       format = ast_getformatbyname(v->value);
+                       if (format < 1) 
+                               ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value);
+                       else
+                               iax_capability &= ~format;
+               }
+               v = v->next;
+       }
+       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")) {
+                                       user = build_user(cat, ast_variable_browse(cfg, cat));
+                                       if (user) {
+                                               pthread_mutex_lock(&userl.lock);
+                                               user->next = userl.users;
+                                               userl.users = user;
+                                               pthread_mutex_unlock(&userl.lock);
+                                       }
+                               } else if (!strcasecmp(utype, "peer")) {
+                                       peer = build_peer(cat, ast_variable_browse(cfg, cat));
+                                       if (peer) {
+                                               pthread_mutex_lock(&peerl.lock);
+                                               peer->next = peerl.peers;
+                                               peerl.peers = peer;
+                                               pthread_mutex_unlock(&peerl.lock);
+                                       }
+                               } else {
+                                       ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, config);
+                               }
+                       } else
+                               ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat);
+               }
+               cat = ast_category_browse(cfg, cat);
+       }
+       ast_destroy(cfg);
+       if (ast_channel_register(type, tdesc, iax_capability, iax_request)) {
+               ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
+               unload_module();
+               return -1;
+       }
+       
+       /* Make a UDP socket */
+       netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+               
+       if (netsocket < 0) {
+               ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
+               return -1;
+       }
+       if (bind(netsocket, &sin, sizeof(sin))) {
+               ast_log(LOG_ERROR, "Unable to bind to %s port %d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+               return -1;
+       }
+
+       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);
+       }
+       return res;
+}
+
+char *description()
+{
+       return desc;
+}
+
+int unload_module()
+{
+       struct iax_user *user, *userlast;
+       struct iax_peer *peer, *peerlast;
+       int x;
+       /* Cancel the network thread, close the net socket */
+       pthread_cancel(netthreadid);
+       pthread_join(netthreadid, NULL);
+       close(netsocket);
+       for (x=0;x<AST_IAX_MAX_CALLS;x++)
+               if (iaxs[x])
+                       iax_destroy(x);
+       ast_cli_unregister(&cli_show_users);
+       ast_cli_unregister(&cli_show_channels);
+       ast_cli_unregister(&cli_show_peers);
+       ast_cli_unregister(&cli_set_jitter);
+#ifdef IAX_SIMULATOR
+       ast_cli_unregister(&delay_cli);
+       ast_cli_unregister(&deviation_cli);
+       ast_cli_unregister(&reliability_cli);
+       ast_cli_unregister(&sim_show_cli);
+#endif
+       for (user=userl.users;user;) {
+               free_ha(user->ha);
+               free_context(user->contexts);
+               userlast = user;
+               user=user->next;
+               free(userlast);
+       }
+       for (peer=peerl.peers;peer;) {
+               peerlast = peer;
+               peer=peer->next;
+               free(peerlast);
+       }
+       return 0;
+}
+
+int usecount()
+{
+       int res;
+       pthread_mutex_lock(&usecnt_lock);
+       res = usecnt;
+       pthread_mutex_unlock(&usecnt_lock);
+       return res;
+}
+
diff --git a/configs/iax.conf.sample b/configs/iax.conf.sample
new file mode 100755 (executable)
index 0000000..62307d5
--- /dev/null
@@ -0,0 +1,72 @@
+;
+; Inter-Asterisk eXchange driver definition
+;
+;
+; General settings, like port number to bind to, and
+; an option address (the default is to bind to all
+; local addresses).
+;
+[general]
+port=5036
+;bindaddr=192.168.0.1
+;
+; Specify bandwidth of low, medium, or high to control which codecs are used
+; in general.
+;
+bandwidth=low
+;
+; You can also fine tune codecs here using "allow" and "disallow" clauses
+; with specific codecs.  Use "all" to represent all formats.
+;
+;allow=all                     ; same as bandwidth=high
+;disallow=g723.1               ; Hm...  Proprietary, don't use it...
+disallow=lpc10                 ; Icky sound quality...  Mr. Roboto.
+;allow=gsm                     ; Always allow GSM, it's cool :)
+;
+; You can also adjust several parameters relating to the jitter
+; buffer.  Specifically, you can provide a maximum jitter buffer,
+; you can turn it off entirely, and you can specify an acceptable
+; drop rate (per MEMORY_SIZE, by default 3 of 100).  Disabling the
+; jitter buffer is not recommended.  Finally, you can specify the maximum
+; excess jitter buffer, which if exceeded, causes the jitter buffer to
+; slowly shrink in order to improve latency.
+;
+;jitterbuffer=no
+;dropcount=3
+;maxjitterbuffer=500
+;maxexccessbuffer=100
+
+;
+; Guest sections for unauthenticated connection attempts.  Just
+; specify an empty secret, or provide no secret section.
+;
+[guest]
+type=user
+context=default
+;
+; Further user sections may be added, specifying a context and a
+; secret used for connections with that given authentication name.
+; Limited IP based access control is allowed by use of "allow" and
+; "deny" keywords.  Multiple rules are permitted.  Multiple permitted
+; contexts may be specified, in which case the first will be the default.
+;
+;[markster]
+;type=user
+;context=default
+;context=local
+;auth=md5,cleartext
+;secret=markpasswd
+;deny=0.0.0.0/0.0.0.0
+;allow=209.16.236.73/255.255.255.0
+;
+; Peers may also be specified, with a secret and
+; a remote hostname.
+;
+[demo]
+type=peer
+username=asterisk
+secret=supersecret
+host=209.16.236.91
+;host=asterisk.linux-support.net
+;port=5036
+;mask=255.255.255.255