Newer
Older
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Implementation of Inter-Asterisk eXchange Version 2
*
* Copyright (C) 2003, Digium
*
* Mark Spencer <markster@digium.com>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <asterisk/lock.h>
#include <asterisk/frame.h>
#include <asterisk/channel.h>
#include <asterisk/channel_pvt.h>
#include <asterisk/logger.h>
#include <asterisk/module.h>
#include <asterisk/pbx.h>
#include <asterisk/sched.h>
#include <asterisk/io.h>
#include <asterisk/config.h>
#include <asterisk/options.h>
#include <asterisk/cli.h>
#include <asterisk/translate.h>
#include <asterisk/md5.h>
#include <asterisk/cdr.h>
#include <asterisk/crypto.h>
#include <asterisk/acl.h>
#include <asterisk/manager.h>
#include <asterisk/callerid.h>
#include <asterisk/musiconhold.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <signal.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#ifdef IAX_TRUNKING
#include <sys/ioctl.h>
#include <linux/zaptel.h>
#endif
#ifdef MYSQL_FRIENDS
#include <mysql/mysql.h>
#endif
#ifndef IPTOS_MINCOST
#define IPTOS_MINCOST 0x02
#endif
/*
* Uncomment to try experimental IAX bridge optimization,
* designed to reduce latency when IAX calls cannot
* be trasnferred
*/
#define BRIDGE_OPTIMIZATION
#define PTR_TO_CALLNO(a) ((unsigned short)(unsigned long)(a))
#define CALLNO_TO_PTR(a) ((void *)(unsigned long)(a))
#define DEFAULT_RETRY_TIME 1000
#define MEMORY_SIZE 100
#define DEFAULT_DROP 3
/* Flag to use with trunk calls, keeping these calls high up. It halves our effective use
but keeps the division between trunked and non-trunked better. */
#define TRUNK_CALL_START 0x4000
#define MIN_REUSE_TIME 60 /* Don't reuse a call number within 60 seconds */
/* Sample over last 100 units to determine historic jitter */
#define GAMMA (0.01)
#ifdef MYSQL_FRIENDS
static ast_mutex_t mysqllock = AST_MUTEX_INITIALIZER;
static MYSQL *mysql;
static char mydbuser[80];
static char mydbpass[80];
static char mydbhost[80];
static char mydbname[80];
#endif
static char *desc = "Inter Asterisk eXchange (Ver 2)";
static char *tdesc = "Inter Asterisk eXchange Driver (Ver 2)";
static char *type = "IAX2";
static char context[80] = "default";
static int max_retries = 4;
static int ping_time = 20;
static int lagrq_time = 10;
static int maxtrunkcall = TRUNK_CALL_START;
static int maxnontrunkcall = 1;
Mark Spencer
committed
static int iaxcompat = 0;
static int iaxdefaultdpcache=10 * 60; /* Cache dialplan entries for 10 minutes by default */
static int iaxdefaulttimeout = 5; /* Default to wait no more than 5 seconds for a reply to come back */
static int netsocket = -1;
static int tos = 0;
static int expirey = IAX_DEFAULT_REG_EXPIRE;
static int timingfd = -1; /* Timing file descriptor */
static ast_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER;
int (*iax2_regfunk)(char *username, int onoff) = NULL;
/* Ethernet, etc */
#define IAX_CAPABILITY_FULLBANDWIDTH 0xFFFF
/* T1, maybe ISDN */
#define IAX_CAPABILITY_MEDBANDWIDTH (IAX_CAPABILITY_FULLBANDWIDTH & \
~AST_FORMAT_SLINEAR & \
~AST_FORMAT_ULAW & \
~AST_FORMAT_ALAW)
/* A modem */
#define IAX_CAPABILITY_LOWBANDWIDTH (IAX_CAPABILITY_MEDBANDWIDTH & \
~AST_FORMAT_G726 & \
~AST_FORMAT_ADPCM)
#define IAX_CAPABILITY_LOWFREE (IAX_CAPABILITY_LOWBANDWIDTH & \
~AST_FORMAT_G723_1)
#define DEFAULT_MAXMS 2000 /* Must be faster than 2 seconds by default */
#define DEFAULT_FREQ_OK 60 * 1000 /* How often to check for the host to be up */
#define DEFAULT_FREQ_NOTOK 10 * 1000 /* How often to check, if the host is down... */
static struct io_context *io;
static struct sched_context *sched;
static int iax2_capability = IAX_CAPABILITY_FULLBANDWIDTH;
static int iax2_dropcount = DEFAULT_DROP;
static int use_jitterbuffer = 0;
static char accountcode[20];
static int amaflags = 0;
static int notransfer = 0;
static pthread_t netthreadid = AST_PTHREADT_NULL;
#define IAX_STATE_STARTED (1 << 0)
#define IAX_STATE_AUTHENTICATED (1 << 1)
#define IAX_STATE_TBD (1 << 2)
struct iax2_context {
char context[AST_MAX_EXTENSION];
struct iax2_context *next;
};
struct iax2_user {
char name[80];
char secret[80];
int authmethods;
char accountcode[20];
char inkeys[80]; /* Key(s) this user can use to authenticate to us */
int amaflags;
int hascallerid;
int temponly;
int capability;
char callerid[AST_MAX_EXTENSION];
struct ast_ha *ha;
struct iax2_context *contexts;
struct iax2_user *next;
int notransfer;
};
struct iax2_peer {
char name[80];
char username[80];
char secret[80];
char outkey[80]; /* What key we use to talk to this peer */
char context[AST_MAX_EXTENSION]; /* Default context (for transfer really) */
char mailbox[AST_MAX_EXTENSION]; /* Mailbox */
struct sockaddr_in addr;
int formats;
struct in_addr mask;
/* Dynamic Registration fields */
int dynamic; /* If this is a dynamic peer */
struct sockaddr_in defaddr; /* Default address if there is one */
int authmethods; /* Authentication methods (IAX_AUTH_*) */
char inkeys[80]; /* Key(s) this peer can use to authenticate to us */
int hascallerid;
/* Suggested caller id if registering */
char callerid[AST_MAX_EXTENSION];
/* Whether or not to send ANI */
int sendani;
int expire; /* Schedule entry for expirey */
int expirey; /* How soon to expire */
int capability; /* Capability */
int delme; /* I need to be deleted */
int temponly; /* I'm only a temp */
int trunk; /* Treat as an IAX trunking */
struct timeval txtrunktime; /* Transmit trunktime */
struct timeval rxtrunktime; /* Receive trunktime */
struct timeval lasttxtime; /* Last transmitted trunktime */
unsigned int lastsent; /* Last sent time */
/* Qualification */
int callno; /* Call number of POKE request */
int pokeexpire; /* When to expire poke */
int lastms; /* How long last response took (in ms), or -1 for no response */
int maxms; /* Max ms we will accept for the host to be up, 0 to not monitor */
struct ast_ha *ha;
struct iax2_peer *next;
int notransfer;
struct iax_firmware {
struct iax_firmware *next;
int fd;
int mmaplen;
int dead;
struct ast_iax2_firmware_header *fwh;
unsigned char *buf;
};
#define REG_STATE_UNREGISTERED 0
#define REG_STATE_REGSENT 1
#define REG_STATE_AUTHSENT 2
#define REG_STATE_REGISTERED 3
#define REG_STATE_REJECTED 4
#define REG_STATE_TIMEOUT 5
#define REG_STATE_NOAUTH 6
#define TRANSFER_NONE 0
#define TRANSFER_BEGIN 1
#define TRANSFER_READY 2
#define TRANSFER_RELEASED 3
#define TRANSFER_PASSTHROUGH 4
struct iax2_registry {
struct sockaddr_in addr; /* Who we connect to for registration purposes */
char username[80];
char secret[80]; /* Password or key name in []'s */
char random[80];
int expire; /* Sched ID of expiration */
int refresh; /* How often to refresh */
int regstate;
int callno; /* Associated call number if applicable */
struct sockaddr_in us; /* Who the server thinks we are */
struct iax2_registry *next;
};
static struct iax2_registry *registrations;
/* Don't retry more frequently than every 10 ms, or less frequently than every 5 seconds */
#define MAX_RETRY_TIME 10000
#define MAX_JITTER_BUFFER 50
#define MAX_TRUNKDATA 640 /* 40ms, uncompressed linear */
/* If we have more than this much excess real jitter buffer, srhink it. */
static int max_jitter_buffer = MAX_JITTER_BUFFER;
struct chan_iax2_pvt {
/* Pipes for communication. pipe[1] belongs to the
network thread (write), and pipe[0] belongs to the individual
channel (read) */
/* Whether or not we Quelch audio */
int quelch;
/* Last received voice format */
int voiceformat;
/* Last received voice format */
int videoformat;
/* Last sent voice format */
int svoiceformat;
/* Last sent video format */
int svideoformat;
/* What we are capable of sending */
int capability;
/* Last received timestamp */
unsigned int last;
/* Last sent timestamp - never send the same timestamp twice in a single call */
unsigned int lastsent;
/* Next outgoing timestamp if everything is good */
unsigned int nextpred;
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/* Ping time */
unsigned int pingtime;
/* Max time for initial response */
int maxtime;
/* Peer Address */
struct sockaddr_in addr;
/* Our call number */
unsigned short callno;
/* Peer callno */
unsigned short peercallno;
/* Peer selected format */
int peerformat;
/* Peer capability */
int peercapability;
/* timeval that we base our transmission on */
struct timeval offset;
/* timeval that we base our delivery on */
struct timeval rxcore;
/* Historical delivery time */
int history[MEMORY_SIZE];
/* Current base jitterbuffer */
int jitterbuffer;
/* Current jitter measure */
int jitter;
/* Historic jitter value */
int historicjitter;
/* LAG */
int lag;
/* Error, as discovered by the manager */
int error;
/* Owner if we have one */
struct ast_channel *owner;
/* What's our state? */
int state;
/* Expirey (optional) */
int expirey;
/* Next outgoing sequence number */
/* Next sequence number they have not yet acknowledged */
/* Last incoming sequence number we have acknowledged */
/* Peer name */
char peer[80];
/* Default Context */
char context[80];
/* Caller ID if available */
char callerid[80];
/* Hidden Caller ID (i.e. ANI) if appropriate */
char ani[80];
/* Whether or not ani should be transmitted in addition to Caller*ID */
int sendani;
/* Whether to request autoanswer */
int autoanswer;
/* DNID */
char dnid[80];
/* Requested Extension */
char exten[AST_MAX_EXTENSION];
/* Expected Username */
char username[80];
/* Expected Secret */
char secret[80];
/* permitted authentication methods */
int authmethods;
/* MD5 challenge */
char challenge[10];
/* Public keys permitted keys for incoming authentication */
char inkeys[80];
/* Private key for outgoing authentication */
char outkey[80];
/* Preferred language */
char language[80];
/* Hostname/peername for naming purposes */
char host[80];
/* Associated registry */
struct iax2_registry *reg;
/* Associated peer for poking */
struct iax2_peer *peerpoke;
/* Transferring status */
int transferring;
/* Transfer identifier */
int transferid;
/* Already disconnected */
int alreadygone;
/* Who we are IAX transfering to */
struct sockaddr_in transfer;
/* What's the new call number for the transfer */
unsigned short transfercallno;
/* Status of knowledge of peer ADSI capability */
int peeradsicpe;
/* Who we are bridged to */
unsigned short bridgecallno;
unsigned int bridgesfmt;
struct ast_trans_pvt *bridgetrans;
int pingid; /* Transmit PING request */
int lagid; /* Retransmit lag request */
int autoid; /* Auto hangup for Dialplan requestor */
int initid; /* Initial peer auto-congest ID (based on qualified peers) */
char dproot[AST_MAX_EXTENSION];
char accountcode[20];
int amaflags;
/* This is part of a trunk interface */
int trunk;
/* Trunk data and length */
unsigned char trunkdata[MAX_TRUNKDATA];
unsigned int trunkdatalen;
int notransfer; /* do we want native bridging */
};
static struct ast_iax2_queue {
struct iax_frame *head;
struct iax_frame *tail;
ast_mutex_t lock;
} iaxq;
static struct ast_user_list {
struct iax2_user *users;
ast_mutex_t lock;
} userl;
static struct ast_peer_list {
struct iax2_peer *peers;
ast_mutex_t lock;
static struct ast_firmware_list {
struct iax_firmware *wares;
ast_mutex_t lock;
} waresl;
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
/* Extension exists */
#define CACHE_FLAG_EXISTS (1 << 0)
/* Extension is non-existant */
#define CACHE_FLAG_NONEXISTANT (1 << 1)
/* Extension can exist */
#define CACHE_FLAG_CANEXIST (1 << 2)
/* Waiting to hear back response */
#define CACHE_FLAG_PENDING (1 << 3)
/* Timed out */
#define CACHE_FLAG_TIMEOUT (1 << 4)
/* Request transmitted */
#define CACHE_FLAG_TRANSMITTED (1 << 5)
/* Timeout */
#define CACHE_FLAG_UNKNOWN (1 << 6)
/* Matchmore */
#define CACHE_FLAG_MATCHMORE (1 << 7)
static struct iax2_dpcache {
char peercontext[AST_MAX_EXTENSION];
char exten[AST_MAX_EXTENSION];
struct timeval orig;
struct timeval expirey;
int flags;
unsigned short callno;
int waiters[256];
struct iax2_dpcache *next;
struct iax2_dpcache *peer; /* For linking in peers */
} *dpcache;
static ast_mutex_t dpcache_lock;
static void iax_debug_output(const char *data)
static void iax_error_output(const char *data)
/* XXX We probably should use a mutex when working with this XXX */
static struct chan_iax2_pvt *iaxs[IAX_MAX_CALLS];
static ast_mutex_t iaxsl[IAX_MAX_CALLS];
static struct timeval lastused[IAX_MAX_CALLS];
static int send_command(struct chan_iax2_pvt *, char, int, unsigned int, char *, int, int);
static int send_command_locked(unsigned short callno, char, int, unsigned int, char *, int, int);
static int send_command_immediate(struct chan_iax2_pvt *, char, int, unsigned int, char *, int, int);
static int send_command_final(struct chan_iax2_pvt *, char, int, unsigned int, char *, int, int);
static int send_command_transfer(struct chan_iax2_pvt *, char, int, unsigned int, char *, int);
static unsigned int calc_timestamp(struct chan_iax2_pvt *p, unsigned int ts, struct ast_frame *f);
static int send_ping(void *data)
{
int callno = (long)data;
/* Ping only if it's real, not if it's bridged */
if (iaxs[callno]) {
#ifdef BRIDGE_OPTIMIZATION
if (!iaxs[callno]->bridgecallno)
#endif
send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_PING, 0, NULL, 0, -1);
return 1;
} else
return 0;
}
static int send_lagrq(void *data)
{
int callno = (long)data;
/* Ping only if it's real not if it's bridged */
if (iaxs[callno]) {
#ifdef BRIDGE_OPTIMIZATION
if (!iaxs[callno]->bridgecallno)
#endif
send_command(iaxs[callno], AST_FRAME_IAX, 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 */
return subclass;
/* Otherwise find its power */
if (subclass & (1 << x)) {
if (power > -1) {
ast_log(LOG_WARNING, "Can't compress subclass %d\n", subclass);
return 0;
} else
power = x;
}
}
}
static int uncompress_subclass(unsigned char csub)
{
/* If the SC_LOG flag is set, return 2^csub otherwise csub */
/* special case for 'compressed' -1 */
if (csub == 0xff)
return -1;
else
return 1 << (csub & ~IAX_FLAG_SC_LOG & IAX_MAX_SHIFT);
static int iax2_getpeername(struct sockaddr_in sin, char *host, int len, int lockpeer)
{
struct iax2_peer *peer;
int res = 0;
if (lockpeer)
ast_mutex_lock(&peerl.lock);
peer = peerl.peers;
while(peer) {
if ((peer->addr.sin_addr.s_addr == sin.sin_addr.s_addr) &&
(peer->addr.sin_port == sin.sin_port)) {
strncpy(host, peer->name, len-1);
res = 1;
break;
}
peer = peer->next;
}
if (lockpeer)
ast_mutex_unlock(&peerl.lock);
return res;
}
static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, int lockpeer)
{
struct chan_iax2_pvt *tmp;
tmp = malloc(sizeof(struct chan_iax2_pvt));
if (tmp) {
memset(tmp, 0, sizeof(struct chan_iax2_pvt));
tmp->callno = 0;
tmp->peercallno = 0;
tmp->transfercallno = 0;
tmp->bridgecallno = 0;
tmp->pingid = -1;
tmp->lagid = -1;
tmp->autoid = -1;
tmp->initid = -1;
/* strncpy(tmp->context, context, sizeof(tmp->context)-1); */
strncpy(tmp->exten, "s", sizeof(tmp->exten)-1);
if (!iax2_getpeername(*sin, tmp->host, sizeof(tmp->host), lockpeer))
snprintf(tmp->host, sizeof(tmp->host), "%s:%d", inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
}
return tmp;
}
static int get_samples(struct ast_frame *f)
{
int samples=0;
switch(f->subclass) {
case AST_FORMAT_SPEEX:
samples = 160; /* XXX Not necessarily true XXX */
break;
case AST_FORMAT_G723_1:
samples = 240 /* XXX Not necessarily true XXX */;
break;
case AST_FORMAT_GSM:
samples = 160 * (f->datalen / 33);
break;
case AST_FORMAT_G729A:
samples = 160 * (f->datalen / 20);
break;
case AST_FORMAT_SLINEAR:
samples = f->datalen / 2;
break;
case AST_FORMAT_LPC10:
samples = 22 * 8;
samples += (((char *)(f->data))[7] & 0x1) * 8;
break;
case AST_FORMAT_ULAW:
samples = f->datalen;
break;
case AST_FORMAT_ALAW:
samples = f->datalen;
break;
case AST_FORMAT_ADPCM:
case AST_FORMAT_G726:
samples = f->datalen *2;
break;
default:
ast_log(LOG_WARNING, "Don't know how to calculate samples on %d packets\n", f->subclass);
}
return samples;
}
static struct iax_frame *iaxfrdup2(struct iax_frame *fr)
struct iax_frame *new = iax_frame_new(DIRECTION_INGRESS, fr->af.datalen);
memcpy(new, fr, sizeof(struct iax_frame));
iax_frame_wrap(new, &fr->af);
new->data = NULL;
new->datalen = 0;
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
new->direction = DIRECTION_INGRESS;
new->retrans = -1;
}
return new;
}
#define NEW_PREVENT 0
#define NEW_ALLOW 1
#define NEW_FORCE 2
static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short dcallno, struct chan_iax2_pvt *cur)
{
if ((cur->addr.sin_addr.s_addr == sin->sin_addr.s_addr) &&
(cur->addr.sin_port == sin->sin_port)) {
/* This is the main host */
if ((cur->peercallno == callno) ||
((dcallno == cur->callno) && !cur->peercallno)) {
/* That's us. Be sure we keep track of the peer call number */
return 1;
}
}
if ((cur->transfer.sin_addr.s_addr == sin->sin_addr.s_addr) &&
(cur->transfer.sin_port == sin->sin_port) && (cur->transferring)) {
/* We're transferring */
if (dcallno == cur->callno)
return 1;
}
return 0;
}
static void update_max_trunk(void)
{
int max = TRUNK_CALL_START;
int x;
/* XXX Prolly don't need locks here XXX */
for (x=TRUNK_CALL_START;x<IAX_MAX_CALLS - 1; x++) {
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
if (iaxs[x])
max = x + 1;
}
maxtrunkcall = max;
if (option_debug)
ast_log(LOG_DEBUG, "New max trunk callno is %d\n", max);
}
static void update_max_nontrunk(void)
{
int max = 1;
int x;
/* XXX Prolly don't need locks here XXX */
for (x=1;x<TRUNK_CALL_START - 1; x++) {
if (iaxs[x])
max = x + 1;
}
maxnontrunkcall = max;
if (option_debug)
ast_log(LOG_DEBUG, "New max nontrunk callno is %d\n", max);
}
static int make_trunk(unsigned short callno, int locked)
{
int x;
int res= 0;
struct timeval now;
if (iaxs[callno]->oseqno) {
ast_log(LOG_WARNING, "Can't make trunk once a call has started!\n");
return -1;
}
if (callno & TRUNK_CALL_START) {
ast_log(LOG_WARNING, "Call %d is already a trunk\n", callno);
return -1;
}
gettimeofday(&now, NULL);
for (x=TRUNK_CALL_START;x<IAX_MAX_CALLS - 1; x++) {
ast_mutex_lock(&iaxsl[x]);
if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) {
iaxs[x] = iaxs[callno];
iaxs[x]->callno = x;
iaxs[callno] = NULL;
/* Update the two timers that should have been started */
if (iaxs[x]->pingid > -1)
ast_sched_del(sched, iaxs[x]->pingid);
if (iaxs[x]->lagid > -1)
ast_sched_del(sched, iaxs[x]->lagid);
iaxs[x]->pingid = ast_sched_add(sched, ping_time * 1000, send_ping, (void *)x);
iaxs[x]->lagid = ast_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)x);
if (locked)
ast_mutex_unlock(&iaxsl[callno]);
ast_mutex_unlock(&iaxsl[x]);
ast_mutex_unlock(&iaxsl[x]);
ast_log(LOG_WARNING, "Unable to trunk call: Insufficient space\n");
return -1;
}
ast_log(LOG_DEBUG, "Made call %d into trunk call %d\n", callno, x);
/* We move this call from a non-trunked to a trunked call */
update_max_trunk();
update_max_nontrunk();
return res;
}
static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int lockpeer)
if (new <= NEW_ALLOW) {
/* Look for an existing connection first */
for (x=1;(res < 1) && (x<maxnontrunkcall);x++) {
ast_mutex_lock(&iaxsl[x]);
if (iaxs[x]) {
/* Look for an exact match */
if (match(sin, callno, dcallno, iaxs[x])) {
res = x;
}
}
ast_mutex_unlock(&iaxsl[x]);
}
for (x=TRUNK_CALL_START;(res < 1) && (x<maxtrunkcall);x++) {
ast_mutex_lock(&iaxsl[x]);
if (iaxs[x]) {
/* Look for an exact match */
if (match(sin, callno, dcallno, iaxs[x])) {
res = x;
}
}
ast_mutex_unlock(&iaxsl[x]);
if ((res < 1) && (new >= NEW_ALLOW)) {
gettimeofday(&now, NULL);
for (x=1;x<TRUNK_CALL_START;x++) {
/* Find first unused call number that hasn't been used in a while */
ast_mutex_lock(&iaxsl[x]);
if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) break;
ast_mutex_unlock(&iaxsl[x]);
}
/* We've still got lock held if we found a spot */
if (x >= TRUNK_CALL_START) {
ast_log(LOG_WARNING, "No more space\n");
return -1;
if (iaxs[x]) {
if (option_debug)
ast_log(LOG_DEBUG, "Creating new call structure %d\n", x);
iaxs[x]->addr.sin_port = sin->sin_port;
iaxs[x]->addr.sin_family = sin->sin_family;
iaxs[x]->addr.sin_addr.s_addr = sin->sin_addr.s_addr;
iaxs[x]->peercallno = callno;
iaxs[x]->callno = x;
iaxs[x]->pingtime = DEFAULT_RETRY_TIME;
iaxs[x]->expirey = expirey;
iaxs[x]->pingid = ast_sched_add(sched, ping_time * 1000, send_ping, (void *)x);
iaxs[x]->lagid = ast_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)x);
iaxs[x]->amaflags = amaflags;
iaxs[x]->notransfer = notransfer;
strncpy(iaxs[x]->accountcode, accountcode, sizeof(iaxs[x]->accountcode)-1);
} else {
ast_log(LOG_WARNING, "Out of resources\n");
res = x;
}
return res;
}
static void iax2_frame_free(struct iax_frame *fr)
{
if (fr->retrans > -1)
ast_sched_del(sched, fr->retrans);
iax_frame_free(fr);
}
static int iax2_queue_frame(int callno, struct ast_frame *f)
{
int pass =0;
/* Assumes lock for callno is already held... */
for (;;) {
pass++;
if (iaxs[callno] && iaxs[callno]->owner) {
if (ast_mutex_trylock(&iaxs[callno]->owner->lock)) {
/* Avoid deadlock by pausing and trying again */
ast_mutex_unlock(&iaxsl[callno]);
ast_mutex_lock(&iaxsl[callno]);
Mark Spencer
committed
ast_queue_frame(iaxs[callno]->owner, f);
ast_mutex_unlock(&iaxs[callno]->owner->lock);
break;
}
} else
break;
}
return 0;
}
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
static void destroy_firmware(struct iax_firmware *cur)
{
/* Close firmware */
if (cur->fwh) {
munmap(cur->fwh, ntohl(cur->fwh->datalen) + sizeof(*(cur->fwh)));
}
close(cur->fd);
free(cur);
}
static int try_firmware(char *s)
{
struct stat stbuf;
struct iax_firmware *cur;
int fd;
int res;
struct ast_iax2_firmware_header *fwh, fwh2;
struct MD5Context md5;
unsigned char sum[16];
res = stat(s, &stbuf);
if (res < 0) {
ast_log(LOG_WARNING, "Failed to stat '%s': %s\n", s, strerror(errno));
return -1;
}
/* Make sure it's not a directory */
if (S_ISDIR(stbuf.st_mode))
return -1;
fd = open(s, O_RDONLY);
if (fd < 0) {
ast_log(LOG_WARNING, "Cannot open '%s': %s\n", s, strerror(errno));
return -1;
}
if ((res = read(fd, &fwh2, sizeof(fwh2))) != sizeof(fwh2)) {
ast_log(LOG_WARNING, "Unable to read firmware header in '%s'\n", s);
close(fd);
return -1;
}
if (ntohl(fwh2.magic) != IAX_FIRMWARE_MAGIC) {
ast_log(LOG_WARNING, "'%s' is not a valid firmware file\n", s);
close(fd);
return -1;
}
if (ntohl(fwh2.datalen) != (stbuf.st_size - sizeof(fwh2))) {
ast_log(LOG_WARNING, "Invalid data length in firmware '%s'\n", s);
close(fd);
return -1;
}
if (fwh2.devname[sizeof(fwh2.devname) - 1] || !strlen(fwh2.devname)) {
ast_log(LOG_WARNING, "No or invalid device type specified for '%s'\n", s);
close(fd);
return -1;
}
fwh = mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (!fwh) {
ast_log(LOG_WARNING, "mmap failed: %s\n", strerror(errno));
close(fd);
return -1;
}
MD5Init(&md5);
MD5Update(&md5, fwh->data, ntohl(fwh->datalen));
MD5Final(sum, &md5);
if (memcmp(sum, fwh->chksum, sizeof(sum))) {
ast_log(LOG_WARNING, "Firmware file '%s' fails checksum\n", s);
munmap(fwh, stbuf.st_size);
close(fd);
return -1;
}
cur = waresl.wares;
while(cur) {
if (!strcmp(cur->fwh->devname, fwh->devname)) {
/* Found a candidate */
if (cur->dead || (ntohs(cur->fwh->version) < ntohs(fwh->version)))
/* The version we have on loaded is older, load this one instead */
break;
/* This version is no newer than what we have. Don't worry about it.
We'll consider it a proper load anyhow though */
munmap(fwh, stbuf.st_size);
close(fd);
return 0;
}
cur = cur->next;
}
if (!cur) {
/* Allocate a new one and link it */
cur = malloc(sizeof(struct iax_firmware));
if (cur) {
memset(cur, 0, sizeof(struct iax_firmware));
cur->fd = -1;
cur->next = waresl.wares;
waresl.wares = cur;
}
}
if (cur) {
if (cur->fwh) {
munmap(cur->fwh, cur->mmaplen);
}
if (cur->fd > -1)
close(cur->fd);
cur->fwh = fwh;
cur->fd = fd;
cur->mmaplen = stbuf.st_size;
cur->dead = 0;
}
return 0;
}
static int iax_check_version(char *dev)
{
int res = 0;
struct iax_firmware *cur;
if (dev && strlen(dev)) {
ast_mutex_lock(&waresl.lock);
cur = waresl.wares;
while(cur) {
if (!strcmp(dev, cur->fwh->devname)) {
res = ntohs(cur->fwh->version);
break;
}
cur = cur->next;
}
ast_mutex_unlock(&waresl.lock);
}
return res;
}
static int iax_firmware_append(struct iax_ie_data *ied, const unsigned char *dev, unsigned int desc)
{