Skip to content
Snippets Groups Projects
chan_iax2.c 294 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Asterisk -- A telephony toolkit for Linux.
     *
     * Implementation of Inter-Asterisk eXchange Version 2
     *
    
     * Copyright (C) 2003 - 2005, Digium, Inc.
    
     *
     * 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/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/features.h"
    #include "asterisk/utils.h"
    #include "asterisk/causes.h"
    #include "asterisk/localtime.h"
    #include "asterisk/aes.h"
    #include "asterisk/dnsmgr.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 <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>
    
    #include <regex.h>
    
    #ifdef IAX_TRUNKING
    #include <sys/ioctl.h>
    
    #ifdef __linux__
    
    #include <linux/zaptel.h>
    
    #else
    #include <zaptel.h>
    #endif /* __linux__ */
    
    #include "iax2.h"
    
    #include "iax2-parser.h"
    
    #include "iax2-provision.h"
    
    /* Define NEWJB to use the new channel independent jitterbuffer,
     * otherwise, use the old jitterbuffer */
    #define NEWJB
    
    #ifdef NEWJB
    #include "../jitterbuf.h"
    #endif
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #ifndef IPTOS_MINCOST
    #define IPTOS_MINCOST 0x02
    #endif
    
    
    #ifdef SO_NO_CHECK
    static int nochecksums = 0;
    #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)
    
    
    static struct ast_codec_pref prefs;
    
    
    static const char desc[] = "Inter Asterisk eXchange (Ver 2)";
    static const char tdesc[] = "Inter Asterisk eXchange Driver (Ver 2)";
    static const char channeltype[] = "IAX2";
    
    
    static char context[80] = "default";
    
    
    static char language[MAX_LANGUAGE] = "";
    
    static char regcontext[AST_MAX_EXTENSION] = "";
    
    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 jittershrinkrate=2;
    
    static int trunkfreq = 20;
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int authdebug = 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int autokill = 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 tos = 0;
    
    
    static int expirey = IAX_DEFAULT_REG_EXPIRE;
    
    static int timingfd = -1;				/* Timing file descriptor */
    
    
    static struct ast_netsock_list netsock;
    static int defaultsockfd = -1;
    
    
    static int usecnt;
    
    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_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 iaxdebug = 0;
    
    
    static int iaxtrunkdebug = 0;
    
    
    static char accountcode[20];
    static int amaflags = 0;
    
    
    static struct ast_flags globalflags = {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;
    };
    
    
    #define IAX_HASCALLERID		(1 << 0)	/* CallerID has been specified */
    #define IAX_DELME		(1 << 1)	/* Needs to be deleted */
    #define IAX_TEMPONLY		(1 << 2)	/* Temporary (realtime) */
    #define IAX_TRUNK		(1 << 3)	/* Treat as a trunk */
    #define IAX_NOTRANSFER		(1 << 4)	/* Don't native bridge */
    #define IAX_USEJITTERBUF	(1 << 5)	/* Use jitter buffer */
    #define IAX_DYNAMIC		(1 << 6)	/* dynamic peer */
    #define IAX_SENDANI		(1 << 7)	/* Send ANI along with CallerID */
    #define IAX_MESSAGEDETAIL	(1 << 8)	/* Show exact numbers */
    #define IAX_ALREADYGONE		(1 << 9)	/* Already disconnected */
    #define IAX_PROVISION		(1 << 10)	/* This is a provisioning request */
    #define IAX_QUELCH		(1 << 11)	/* Whether or not we quelch audio */
    
    #define IAX_ENCRYPTED		(1 << 12)	/* Whether we should assume encrypted tx/rx */
    #define IAX_KEYPOPULATED 	(1 << 13)	/* Whether we have a key populated */
    #define IAX_CODEC_USER_FIRST 	(1 << 14) 	/* are we willing to let the other guy choose the codec? */
    #define IAX_CODEC_NOPREFS 	(1 << 15) 	/* Force old behaviour by turning off prefs */
    #define IAX_CODEC_NOCAP 	(1 << 16) 	/* only consider requested format and ignore capabilities*/
    #define IAX_RTCACHEFRIENDS 	(1 << 17) 	/* let realtime stay till your reload */
    #define IAX_RTNOUPDATE 		(1 << 18) 	/* Don't send a realtime update */
    #define IAX_RTAUTOCLEAR 	(1 << 19) 	/* erase me on expire */ 
    
    #define IAX_FORCEJITTERBUF	(1 << 20)	/* Force jitterbuffer, even when bridged to a channel that can take jitter */ 
    
    
    static int global_rtautoclear = 120;
    
    
    static struct iax2_peer *realtime_peer(const char *peername);
    
    static int reload_config(void);
    
    static int iax2_reload(int fd, int argc, char *argv[]);
    
    
    struct iax2_user {
    	char name[80];
    	char secret[80];
    
    	char dbsecret[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;
    
    	unsigned int flags;
    
    	char cid_num[AST_MAX_EXTENSION];
    	char cid_name[AST_MAX_EXTENSION];
    
    	struct ast_codec_pref prefs;
    
    	struct ast_ha *ha;
    	struct iax2_context *contexts;
    	struct iax2_user *next;
    
    	struct ast_variable *vars;
    
    };
    
    struct iax2_peer {
    	char name[80];
    	char username[80];		
    	char secret[80];
    
    	char dbsecret[80];
    
    	char outkey[80];				/* What key we use to talk to this peer */
    	char context[AST_MAX_EXTENSION];		/* For transfers only */
    	char regexten[AST_MAX_EXTENSION];		/* Extension to register (if regcontext is used) */
    	char peercontext[AST_MAX_EXTENSION];		/* Context to pass to peer */
    	char mailbox[AST_MAX_EXTENSION];		/* Mailbox */
    
    	struct ast_codec_pref prefs;
    
    	struct ast_dnsmgr_entry *dnsmgr;		/* DNS refresh manager */
    
    	struct sockaddr_in addr;
    	int formats;
    
    	int sockfd;					/* Socket to use for transmission */
    
    	struct in_addr mask;
    
    	unsigned int flags;
    
    
    	/* Dynamic Registration fields */
    
    	struct sockaddr_in defaddr;			/* Default address if there is one */
    
    	int authmethods;				/* Authentication methods (IAX_AUTH_*) */
    
    	int encmethods;					/* Encryption methods (IAX_ENCRYPT_*) */
    
    	char inkeys[80];				/* Key(s) this peer can use to authenticate to us */
    
    	/* Suggested caller id if registering */
    
    	char cid_num[AST_MAX_EXTENSION];		/* Default context (for transfer really) */
    	char cid_name[AST_MAX_EXTENSION];		/* Default context (for transfer really) */
    
    	int expire;					/* Schedule entry for expirey */
    
    	int expirey;					/* How soon to expire */
    	int capability;					/* Capability */
    
    	char zonetag[80];				/* Time Zone */
    
    
    	/* 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;
    
    
    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 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 MIN_JITTER_BUFFER 10
    
    #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 consecutive voice frame timestamps jump by more than this many milliseconds, then jitter buffer will resync */
    #define TS_GAP_FOR_JB_RESYNC	5000
    
    
    /* If we have more than this much excess real jitter buffer, shrink it. */
    
    static int max_jitter_buffer = MAX_JITTER_BUFFER;
    
    /* If we have less than this much excess real jitter buffer, enlarge it. */
    static int min_jitter_buffer = MIN_JITTER_BUFFER;
    
    struct iax_rr {
    	int jitter;
    	int losspct;
    	int losscnt;
    	int packets;
    	int delay;
    	int dropped;
    	int ooo;
    };
    
    
    struct chan_iax2_pvt {
    
    	/* Socket to send/receive on for this call */
    	int sockfd;
    
    	/* 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;
    
    	/* True if the last voice we transmitted was not silence/CNG */
    	int notsilenttx;
    
    	/* Ping time */
    	unsigned int pingtime;
    	/* Max time for initial response */
    	int maxtime;
    	/* Peer Address */
    	struct sockaddr_in addr;
    
    	struct ast_codec_pref prefs;
    
    	/* 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;
    
    #ifdef NEWJB
    	/* The jitterbuffer */
            jitterbuf *jb;
    	/* active jb read scheduler id */
            int jbid;                       
    #else
    
    	/* 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 cid_num[80];
    	char cid_name[80];
    
    	/* Hidden Caller ID (i.e. ANI) if appropriate */
    	char ani[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 */
    	int authmethods;
    
    	/* permitted encryption methods */
    	int encmethods;
    
    	/* MD5 challenge */
    	char challenge[10];
    	/* Public keys permitted keys for incoming authentication */
    	char inkeys[80];
    	/* Private key for outgoing authentication */
    	char outkey[80];
    
    	/* Encryption AES-128 Key */
    	aes_encrypt_ctx ecx;
    	/* Decryption AES-128 Key */
    	aes_decrypt_ctx dcx;
    	/* 32 bytes of semi-random data */
    	char semirand[32];
    
    	/* 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;
    
    	unsigned int flags;
    
    	/* Transferring status */
    	int transferring;
    
    	/* Transfer identifier */
    	int transferid;
    
    	/* Who we are IAX transfering to */
    	struct sockaddr_in transfer;
    	/* What's the new call number for the transfer */
    	unsigned short transfercallno;
    
    	/* Transfer decrypt AES-128 Key */
    	aes_encrypt_ctx tdcx;
    
    
    	/* 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 authid;			/* Authentication rejection ID */
    	int authfail;		/* Reason to report failure */
    
    	int initid;			/* Initial peer auto-congest ID (based on qualified peers) */
    
    	int calling_ton;
    	int calling_tns;
    	int calling_pres;
    
    	char dproot[AST_MAX_EXTENSION];
    	char accountcode[20];
    	int amaflags;
    	struct iax2_dpcache *dpentries;
    
    	struct ast_variable *vars;
    
    	/* last received remote rr */
    	struct iax_rr remote_rr;
    	/* Current base time: (just for stats) */
    	int min;
    	/* Dropped frame count: (just for stats) */
    	int frames_dropped;
    	/* received frame count: (just for stats) */
    	int frames_received;
    
    };
    
    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 nonexistent */
    #define CACHE_FLAG_NONEXISTENT	(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 destroy_peer(struct iax2_peer *peer);
    
    static int ast_cli_netstats(int fd, int limit_fmt);
    
    static void iax_debug_output(const char *data)
    
    	if (iaxdebug)
    
    James Golovich's avatar
    James Golovich committed
    		ast_verbose("%s", data);
    
    static void iax_error_output(const char *data)
    
    James Golovich's avatar
    James Golovich committed
    	ast_log(LOG_WARNING, "%s", data);
    
    #ifdef NEWJB
    static void jb_error_output(const char *fmt, ...)
    {
    	va_list args;
    	char buf[1024];
    
    	va_start(args, fmt);
    	vsnprintf(buf, 1024, fmt, args);
    	va_end(args);
    
    	ast_log(LOG_ERROR, buf);
    }
    
    static void jb_warning_output(const char *fmt, ...)
    {
    	va_list args;
    	char buf[1024];
    
    	va_start(args, fmt);
    	vsnprintf(buf, 1024, fmt, args);
    	va_end(args);
    
    	ast_log(LOG_WARNING, buf);
    }
    
    static void jb_debug_output(const char *fmt, ...)
    {
    	va_list args;
    	char buf[1024];
    	if(!iaxdebug) return;
    
    	va_start(args, fmt);
    	vsnprintf(buf, 1024, fmt, args);
    	va_end(args);
    
    	ast_verbose(buf);
    }
    #endif
    
    
    
    /* 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, const char *, int, int);
    static int send_command_locked(unsigned short callno, char, int, unsigned int, const char *, int, int);
    static int send_command_immediate(struct chan_iax2_pvt *, char, int, unsigned int, const char *, int, int);
    static int send_command_final(struct chan_iax2_pvt *, char, int, unsigned int, const char *, int, int);
    static int send_command_transfer(struct chan_iax2_pvt *, char, int, unsigned int, const char *, int);
    
    static struct iax2_user *build_user(const char *name, struct ast_variable *v, int temponly);
    static void destroy_user(struct iax2_user *user);
    static int expire_registry(void *data);
    static int iax2_write(struct ast_channel *c, struct ast_frame *f);
    static int iax2_do_register(struct iax2_registry *reg);
    static void prune_peers(void);
    static int iax2_poke_peer(struct iax2_peer *peer, int heldcall);
    static int iax2_provision(struct sockaddr_in *end, char *dest, const char *template, int force);
    
    static struct ast_channel *iax2_request(const char *type, int format, void *data, int *cause);
    static int iax2_devicestate(void *data);
    static int iax2_digit(struct ast_channel *c, char digit);
    
    static int iax2_sendtext(struct ast_channel *c, const char *text);
    
    static int iax2_sendimage(struct ast_channel *c, struct ast_frame *img);
    
    static int iax2_sendhtml(struct ast_channel *c, int subclass, const char *data, int datalen);
    
    static int iax2_call(struct ast_channel *c, char *dest, int timeout);
    static int iax2_hangup(struct ast_channel *c);
    static int iax2_answer(struct ast_channel *c);
    static struct ast_frame *iax2_read(struct ast_channel *c);
    static int iax2_write(struct ast_channel *c, struct ast_frame *f);
    static int iax2_indicate(struct ast_channel *c, int condition);
    static int iax2_setoption(struct ast_channel *c, int option, void *data, int datalen);
    static int iax2_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc);
    
    static int iax2_transfer(struct ast_channel *c, const char *dest);
    
    static int iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan);
    
    static const struct ast_channel_tech iax2_tech = {
    	.type = channeltype,
    	.description = tdesc,
    	.capabilities = IAX_CAPABILITY_FULLBANDWIDTH,
    
    	.requester = iax2_request,
    	.devicestate = iax2_devicestate,
    	.send_digit = iax2_digit,
    	.send_text = iax2_sendtext,
    	.send_image = iax2_sendimage,
    	.send_html = iax2_sendhtml,
    	.call = iax2_call,
    	.hangup = iax2_hangup,
    	.answer = iax2_answer,
    	.read = iax2_read,
    	.write = iax2_write,
    	.write_video = iax2_write,
    	.indicate = iax2_indicate,
    	.setoption = iax2_setoption,
    	.bridge = iax2_bridge,
    	.transfer = iax2_transfer,
    	.fixup = iax2_fixup,
    };
    
    
    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 get_encrypt_methods(const char *s)
    {
    	int e;
    	if (!strcasecmp(s, "aes128"))
    		e = IAX_ENCRYPT_AES128;
    	else if (ast_true(s))
    		e = IAX_ENCRYPT_AES128;
    	else
    		e = 0;
    	return e;
    }
    
    
    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 struct iax2_peer *find_peer(const char *name, int realtime) 
    
    {
    	struct iax2_peer *peer;
    	ast_mutex_lock(&peerl.lock);
    	for(peer = peerl.peers; peer; peer = peer->next) {
    		if (!strcasecmp(peer->name, name)) {
    			break;
    		}
    	}
    	ast_mutex_unlock(&peerl.lock);
    
    	if(!peer && realtime)
    
    		peer = realtime_peer(name);
    	return peer;
    }
    
    
    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)
    
    static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, int lockpeer, const char *host)
    
    {
    	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);
    
    		strncpy(tmp->host, host, sizeof(tmp->host)-1);
    
    #ifdef NEWJB
    		{
    			jb_info jbinfo;
    
    			tmp->jb = jb_new();
    			tmp->jbid = -1;
    			jbinfo.max_jitterbuf = maxjitterbuffer;
    			jb_setinfo(tmp->jb,&jbinfo);
    		}
    #endif
    
    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 *)(long)x);
    			iaxs[x]->lagid = ast_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)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 sockfd)
    
    	int res = 0;
    
    	struct timeval now;
    
    	char iabuf[INET_ADDRSTRLEN];
    	char host[80];
    
    	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])) {