Skip to content
Snippets Groups Projects
chan_iax2.c 215 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * 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/app.h>
    
    #include <asterisk/astdb.h>
    
    #include <asterisk/musiconhold.h>
    
    #include <asterisk/parking.h>
    
    #include <asterisk/utils.h>
    
    #include <sys/mman.h>
    
    #include <arpa/inet.h>
    
    #include <dirent.h>
    
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <netinet/in_systm.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>
    
    #include <sys/types.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <sys/stat.h>
    
    #ifdef IAX_TRUNKING
    #include <sys/ioctl.h>
    
    #ifdef __linux__
    
    #include <linux/zaptel.h>
    
    #else
    #include <zaptel.h>
    #endif /* __linux__ */
    
    #ifdef MYSQL_FRIENDS
    #include <mysql/mysql.h>
    #endif
    
    #include "iax2.h"
    
    #include "iax2-parser.h"
    
    #include "../astconf.h"
    
    Mark Spencer's avatar
    Mark Spencer committed
    #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 DEBUG_SUPPORT
    
    
    #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 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=3000;
    
    static int trunkfreq = 20;
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int authdebug = 1;
    
    
    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 int usecnt;
    
    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_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 int iaxdebug = 0;
    
    
    static int iaxtrunkdebug = 0;
    
    
    static char accountcode[20];
    static int amaflags = 0;
    
    static int globalnotransfer = 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 amaflags;
    	int hascallerid;
    
    	int delme;
    
    	int trunk;
    
    	char callerid[AST_MAX_EXTENSION];
    	struct ast_ha *ha;
    	struct iax2_context *contexts;
    	struct iax2_user *next;
    
    };
    
    struct iax2_peer {
    	char name[80];
    	char username[80];		
    	char secret[80];
    	char outkey[80];		/* What key we use to talk to this peer */
    	char context[AST_MAX_EXTENSION];	/* Default context (for transfer really) */
    
    	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 */
    
    
    	/* Qualification */
    	int callno;					/* Call number of POKE request */
    	int pokeexpire;					/* When to expire poke */
    	int lastms;					/* How long last response took (in ms), or -1 for no response */
    	int maxms;					/* Max ms we will accept for the host to be up, 0 to not monitor */
    	
    	struct ast_ha *ha;
    	struct iax2_peer *next;
    
    #define 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;
    
    static ast_mutex_t tpeerlock = AST_MUTEX_INITIALIZER;
    
    
    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 messages;					/* Message count */
    
    	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 MIN_RETRY_TIME	100
    
    #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 */
    
    /* 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;
    
    	/* Ping time */
    	unsigned int pingtime;
    	/* Max time for initial response */
    	int maxtime;
    	/* Peer Address */
    	struct sockaddr_in addr;
    	/* Our call number */
    	unsigned short callno;
    	/* Peer callno */
    	unsigned short peercallno;
    	/* Peer selected format */
    	int peerformat;
    	/* Peer capability */
    	int peercapability;
    	/* timeval that we base our transmission on */
    	struct timeval offset;
    	/* timeval that we base our delivery on */
    	struct timeval rxcore;
    	/* Historical delivery time */
    	int history[MEMORY_SIZE];
    	/* Current base jitterbuffer */
    	int jitterbuffer;
    	/* Current jitter measure */
    	int jitter;
    	/* Historic jitter value */
    	int historicjitter;
    	/* LAG */
    	int lag;
    	/* Error, as discovered by the manager */
    	int error;
    	/* Owner if we have one */
    	struct ast_channel *owner;
    	/* What's our state? */
    	int state;
    	/* Expirey (optional) */
    	int expirey;
    	/* Next outgoing sequence number */
    
    	unsigned char oseqno;
    
    	/* Next sequence number they have not yet acknowledged */
    
    	unsigned char rseqno;
    
    	/* Next incoming sequence number */
    
    	unsigned char iseqno;
    
    	/* Last incoming sequence number we have acknowledged */
    
    	unsigned char aseqno;
    
    	/* Peer name */
    	char peer[80];
    	/* Default Context */
    	char context[80];
    	/* Caller ID if available */
    	char callerid[80];
    	/* Hidden Caller ID (i.e. ANI) if appropriate */
    	char ani[80];
    	/* Whether or not ani should be transmitted in addition to Caller*ID */
    	int sendani;
    
    	/* 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 */
    	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;
    
    	struct iax2_dpcache *dpentries;
    
    	int notransfer;		/* do we want native bridging */
    
    };
    
    static struct ast_iax2_queue {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct iax_frame *head;
    	struct iax_frame *tail;
    
    	int count;
    
    } iaxq;
    
    static struct ast_user_list {
    	struct iax2_user *users;
    
    } userl;
    
    static struct ast_peer_list {
    	struct iax2_peer *peers;
    
    static struct ast_firmware_list {
    	struct iax_firmware *wares;
    	ast_mutex_t lock;
    } waresl;
    
    
    /* 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 void iax_debug_output(const char *data)
    
    	if (iaxdebug)
    		ast_verbose(data);
    
    static void iax_error_output(const char *data)
    
    	ast_log(LOG_WARNING, 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 */
    
    	if (subclass < IAX_FLAG_SC_LOG)
    
    		return subclass;
    	/* Otherwise find its power */
    
    	for (x = 0; x < IAX_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 | IAX_FLAG_SC_LOG;
    
    }
    
    static int uncompress_subclass(unsigned char csub)
    {
    	/* If the SC_LOG flag is set, return 2^csub otherwise csub */
    
    	if (csub & IAX_FLAG_SC_LOG) {
    
    		/* 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)
    
    	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)
    
    	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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	case AST_FORMAT_ILBC:
    
    Mark Spencer's avatar
    Mark Spencer committed
    		samples = 240 * (f->datalen / 50);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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:
    
    		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;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static struct iax_frame *iaxfrdup2(struct iax_frame *fr)
    
    {
    	/* Malloc() a copy of a frame */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct iax_frame *new = iax_frame_new(DIRECTION_INGRESS, fr->af.datalen);
    
    	if (new) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		memcpy(new, fr, sizeof(struct iax_frame));	
    		iax_frame_wrap(new, &fr->af);
    
    		new->data = NULL;
    		new->datalen = 0;
    
    		new->direction = DIRECTION_INGRESS;
    		new->retrans = -1;
    	}
    	return new;
    }
    
    #define NEW_PREVENT 0
    #define NEW_ALLOW 	1
    #define NEW_FORCE 	2
    
    static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short dcallno, struct chan_iax2_pvt *cur)
    {
    	if ((cur->addr.sin_addr.s_addr == sin->sin_addr.s_addr) &&
    		(cur->addr.sin_port == sin->sin_port)) {
    		/* This is the main host */
    		if ((cur->peercallno == callno) ||
    			((dcallno == cur->callno) && !cur->peercallno)) {
    			/* That's us.  Be sure we keep track of the peer call number */
    			return 1;
    		}
    	}
    	if ((cur->transfer.sin_addr.s_addr == sin->sin_addr.s_addr) &&
    	    (cur->transfer.sin_port == sin->sin_port) && (cur->transferring)) {
    		/* We're transferring */
    		if (dcallno == cur->callno)
    			return 1;
    	}
    	return 0;
    }
    
    
    static 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++) {
    
    		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++) {
    
    		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)
    
    			res = x;
    			if (!locked)
    
    	if (x >= IAX_MAX_CALLS - 1) {
    
    		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)
    
    	int res = 0;
    
    	struct timeval now;
    
    	if (new <= NEW_ALLOW) {
    		/* Look for an existing connection first */
    
    		for (x=1;(res < 1) && (x<maxnontrunkcall);x++) {
    
    			if (iaxs[x]) {
    				/* Look for an exact match */
    				if (match(sin, callno, dcallno, iaxs[x])) {
    					res = x;
    				}
    			}
    
    		}
    		for (x=TRUNK_CALL_START;(res < 1) && (x<maxtrunkcall);x++) {
    
    			if (iaxs[x]) {
    				/* Look for an exact match */
    				if (match(sin, callno, dcallno, iaxs[x])) {
    					res = 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 */
    
    			if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) break;
    
    		}
    		/* 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;
    
    		iaxs[x] = new_iax(sin, lockpeer);
    
    		update_max_nontrunk();
    
    		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 = globalnotransfer;
    
    			strncpy(iaxs[x]->accountcode, accountcode, sizeof(iaxs[x]->accountcode)-1);
    		} else {
    			ast_log(LOG_WARNING, "Out of resources\n");
    
    			ast_mutex_unlock(&iaxsl[x]);
    
    			return 0;
    
    		ast_mutex_unlock(&iaxsl[x]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    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 */
    
    				usleep(1);
    
    			} else {
    
    				ast_queue_frame(iaxs[callno]->owner, f);
    
    				ast_mutex_unlock(&iaxs[callno]->owner->lock);
    
    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)) {
    
    		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;
    }