asterisk/channels/chan_iax.c

2248 lines
65 KiB
C
Raw Normal View History

/*
* 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;
int formats;
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 10 times as long */
f->retrytime *= 10;
if (f->retrytime > MAX_RETRY_TIME)
f->retrytime = MAX_RETRY_TIME;
ast_sched_add(sched, f->retrytime, attempt_transmit, f);
res=0;
}
}
/* 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 iax_sendtext(struct ast_channel *c, char *text)
{
return send_command(c->pvt->pvt, AST_FRAME_TEXT,
0, 0, text, strlen(text) + 1, -1);
}
static int 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->send_text = iax_sendtext;
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;
}