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 <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>
#else
#include <zaptel.h>
#endif /* __linux__ */
#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
AST_MUTEX_DEFINE_STATIC(mysqllock);
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 char language[MAX_LANGUAGE] = "";
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;
static int maxjitterbuffer=1000;
static int minjitterbuffer=10;
static int jittershrinkrate=2;
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 */
AST_MUTEX_DEFINE_STATIC(usecnt_lock);
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;
Mark Spencer
committed
static int delayreject = 0;
static int globalmessagedetail = 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 */
char language[MAX_LANGUAGE];
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 peercontext[AST_MAX_EXTENSION]; /* Context to pass to peer */
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 */
int messagedetail; /* Show exact numbers? */
/* 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;
#define IAX2_TRUNK_PREFACE (sizeof(struct iax_frame) + sizeof(struct ast_iax2_meta_hdr) + sizeof(struct ast_iax2_meta_trunk_hdr))
static struct iax2_trunk_peer {
ast_mutex_t lock;
struct sockaddr_in addr;
struct timeval txtrunktime; /* Transmit trunktime */
struct timeval rxtrunktime; /* Receive trunktime */
struct timeval lasttxtime; /* Last transmitted trunktime */
struct timeval trunkact; /* Last trunk activity */
unsigned int lastsent; /* Last sent time */
/* Trunk data and length */
unsigned char *trunkdata;
unsigned int trunkdatalen;
unsigned int trunkdataalloc;
struct iax2_trunk_peer *next;
int trunkerror;
int calls;
} *tpeers = NULL;
AST_MUTEX_DEFINE_STATIC(tpeerlock);
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 DEFAULT_TRUNKDATA 640 * 10 /* 40ms, uncompressed linear * 10 channels */
#define MAX_TRUNKDATA 640 * 200 /* 40ms, uncompressed linear * 200 channels */
#define MAX_TIMESTAMP_SKEW 640
/* 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;
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
/* 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[MAX_LANGUAGE];
/* 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 */
Mark Spencer
committed
int authid; /* Authentication rejection ID */
int authfail; /* Reason to report failure */
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;
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;
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
/* 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;
Mark Spencer
committed
AST_MUTEX_DEFINE_STATIC(dpcache_lock);
static void iax_debug_output(const char *data)
if (iaxdebug)
ast_verbose(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)
char iabuf[INET_ADDRSTRLEN];
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;
Mark Spencer
committed
tmp->authid = -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))
Mark Spencer
committed
snprintf(tmp->host, sizeof(tmp->host), "%s:%d", ast_inet_ntoa(iabuf, sizeof(iabuf), 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;
704
705
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
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++) {
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
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 *)(long)x);
iaxs[x]->lagid = ast_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x);
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 *)(long)x);
iaxs[x]->lagid = ast_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x);
iaxs[x]->notransfer = globalnotransfer;
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)
{
/* Assumes lock for callno is already held... */
for (;;) {
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;
}
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
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] || ast_strlen_zero(fwh2.devname)) {
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
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);