Newer
Older
* Asterisk -- An open source telephony toolkit.
* Copyright (C) 1999 - 2006, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
*
* \brief Implementation of Inter-Asterisk eXchange Version 2
*
* \author Mark Spencer <markster@digium.com>
*
* \par See also
* \arg \ref Config_iax
*
* \ingroup channel_drivers
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <dirent.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#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__ */
#endif
Kevin P. Fleming
committed
#include "asterisk.h"
Kevin P. Fleming
committed
Kevin P. Fleming
committed
#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"
Kevin P. Fleming
committed
#include "asterisk/devicestate.h"
Kevin P. Fleming
committed
#include "asterisk/netsock.h"
#include "asterisk/stringfields.h"
Kevin P. Fleming
committed
Mark Spencer
committed
/* Define NEWJB to use the new channel independent jitterbuffer,
* otherwise, use the old jitterbuffer */
#define NEWJB
/* Define SCHED_MULTITHREADED to run the scheduler in a special
multithreaded mode. */
/* Define DEBUG_SCHED_MULTITHREADED to keep track of where each
thread is actually doing. */
Mark Spencer
committed
#ifdef NEWJB
#include "../jitterbuf.h"
#endif
#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
#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)
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 char context[80] = "default";
static char language[MAX_LANGUAGE] = "";
static char regcontext[AST_MAX_CONTEXT] = "";
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;
#ifdef NEWJB
static int resyncthreshold=1000;
Kevin P. Fleming
committed
static int maxjitterinterps=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 tos = 0;
Kevin P. Fleming
committed
static int min_reg_expire;
static int max_reg_expire;
static int timingfd = -1; /* Timing file descriptor */
Kevin P. Fleming
committed
static struct ast_netsock_list *netsock;
static int defaultsockfd = -1;
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)
#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 iaxdebug = 0;
Mark Spencer
committed
static int test_losspct = 0;
#ifdef IAXTESTS
static int test_late = 0;
static int test_resync = 0;
static int test_jit = 0;
static int test_jitpct = 0;
#endif /* IAXTESTS */
Mark Spencer
committed
static char accountcode[AST_MAX_ACCOUNT_CODE];
Mark Spencer
committed
static int delayreject = 0;
Mark Spencer
committed
static int iax2_encryption = 0;
static struct ast_flags globalflags = { 0 };
static pthread_t netthreadid = AST_PTHREADT_NULL;
static pthread_t schedthreadid = AST_PTHREADT_NULL;
Kevin P. Fleming
committed
AST_MUTEX_DEFINE_STATIC(sched_lock);
static int sched_halt = 0;
static ast_cond_t sched_cond;
enum {
IAX_STATE_STARTED = (1 << 0),
IAX_STATE_AUTHENTICATED = (1 << 1),
IAX_STATE_TBD = (1 << 2),
IAX_STATE_UNCHANGED = (1 << 3),
char context[AST_MAX_CONTEXT];
enum {
IAX_HASCALLERID = (1 << 0), /*!< CallerID has been specified */
IAX_DELME = (1 << 1), /*!< Needs to be deleted */
IAX_TEMPONLY = (1 << 2), /*!< Temporary (realtime) */
IAX_TRUNK = (1 << 3), /*!< Treat as a trunk */
IAX_NOTRANSFER = (1 << 4), /*!< Don't native bridge */
IAX_USEJITTERBUF = (1 << 5), /*!< Use jitter buffer */
IAX_DYNAMIC = (1 << 6), /*!< dynamic peer */
IAX_SENDANI = (1 << 7), /*!< Send ANI along with CallerID */
Russell Bryant
committed
/* (1 << 8) is currently unused due to the deprecation of an old option. Go ahead, take it! */
IAX_ALREADYGONE = (1 << 9), /*!< Already disconnected */
IAX_PROVISION = (1 << 10), /*!< This is a provisioning request */
IAX_QUELCH = (1 << 11), /*!< Whether or not we quelch audio */
IAX_ENCRYPTED = (1 << 12), /*!< Whether we should assume encrypted tx/rx */
IAX_KEYPOPULATED = (1 << 13), /*!< Whether we have a key populated */
IAX_CODEC_USER_FIRST = (1 << 14), /*!< are we willing to let the other guy choose the codec? */
IAX_CODEC_NOPREFS = (1 << 15), /*!< Force old behaviour by turning off prefs */
IAX_CODEC_NOCAP = (1 << 16), /*!< only consider requested format and ignore capabilities*/
IAX_RTCACHEFRIENDS = (1 << 17), /*!< let realtime stay till your reload */
IAX_RTUPDATE = (1 << 18), /*!< Send a realtime update */
IAX_RTAUTOCLEAR = (1 << 19), /*!< erase me on expire */
IAX_FORCEJITTERBUF = (1 << 20), /*!< Force jitterbuffer, even when bridged to a channel that can take jitter */
IAX_RTIGNOREREGEXPIRE = (1 << 21), /*!< When using realtime, ignore registration expiration */
IAX_TRUNKTIMESTAMPS = (1 << 22) /*!< Send trunk timestamps */
} iax2_flags;
static int global_rtautoclear = 120;
static int reload_config(void);
Kevin P. Fleming
committed
static int iax2_reload(int fd, int argc, char *argv[]);
struct iax2_user {
char name[80];
char secret[80];
Mark Spencer
committed
int encmethods;
char accountcode[AST_MAX_ACCOUNT_CODE];
char inkeys[80]; /*!< Key(s) this user can use to authenticate to us */
char language[MAX_LANGUAGE];
int capability;
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 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_CONTEXT]; /*!< 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 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 expiry */
int expiry; /*!< How soon to expire */
int capability; /*!< Capability */
char zonetag[80]; /*!< Time Zone */
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 */
int pokefreqok; /*!< How often to check if the host is up */
int pokefreqnotok; /*!< How often to check when the host has been determined to be down */
int historicms; /*!< How long recent average responses took */
int smoothing; /*!< Sample over how many units to determine historic ms */
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;
int sockfd;
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;
};
enum iax_reg_state {
REG_STATE_UNREGISTERED = 0,
REG_STATE_REGSENT,
REG_STATE_AUTHSENT,
REG_STATE_REGISTERED,
REG_STATE_REJECTED,
REG_STATE_TIMEOUT,
REG_STATE_NOAUTH
};
enum iax_transfer_state {
TRANSFER_NONE = 0,
TRANSFER_BEGIN,
TRANSFER_READY,
TRANSFER_RELEASED,
TRANSFER_PASSTHROUGH
};
struct sockaddr_in addr; /*!< Who we connect to for registration purposes */
char secret[80]; /*!< Password or key name in []'s */
int expire; /*!< Sched ID of expiration */
int refresh; /*!< How often to refresh */
enum iax_reg_state regstate;
int messages; /*!< Message count */
int callno; /*!< Associated call number if applicable */
struct sockaddr_in us; /*!< Who the server thinks we are */
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 160 /*!< maximum difference between actual and predicted ts for sending */
/* 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;
static int iaxthreadcount = DEFAULT_THREAD_COUNT;
struct iax_rr {
int jitter;
int losspct;
int losscnt;
int packets;
int delay;
int dropped;
int ooo;
};
/*! Socket to send/receive on for this call */
int sockfd;
/*! Last received voice format */
/*! Last sent voice format */
/*! Last sent video format */
/*! What we are capable of sending */
/*! Last received timestamp */
/*! Last sent timestamp - never send the same timestamp twice in a single call */
/*! Next outgoing timestamp if everything is good */
/*! True if the last voice we transmitted was not silence/CNG */
/*! Ping time */
/*! Max time for initial response */
/*! Peer Address */
struct ast_codec_pref prefs;
/*! Our call number */
/*! Peer callno */
/*! Peer selected format */
/*! Peer capability */
/*! timeval that we base our transmission on */
/*! timeval that we base our delivery on */
Mark Spencer
committed
#ifdef NEWJB
/*! The jitterbuffer */
Mark Spencer
committed
jitterbuf *jb;
/*! active jb read scheduler id */
Mark Spencer
committed
int jbid;
#else
/*! Historical delivery time */
/*! Current base jitterbuffer */
/*! Current jitter measure */
/*! Historic jitter value */
Mark Spencer
committed
#endif
/*! LAG */
/*! Error, as discovered by the manager */
/*! Owner if we have one */
/*! What's our state? */
struct ast_flags state;
/*! Expiry (optional) */
Kevin P. Fleming
committed
int expiry;
/*! Next outgoing sequence number */
/*! Next sequence number they have not yet acknowledged */
/*! Next incoming sequence number */
/*! Last incoming sequence number we have acknowledged */
/*! Peer name */
/*! Default Context */
/*! Caller ID if available */
char cid_num[80];
char cid_name[80];
/*! Hidden Caller ID (i.e. ANI) if appropriate */
/*! DNID */
/*! RDNIS */
char rdnis[80];
/*! Requested Extension */
/*! Expected Username */
/*! Expected Secret */
/*! permitted authentication methods */
/*! permitted encryption methods */
Mark Spencer
committed
int encmethods;
/*! MD5 challenge */
/*! Public keys permitted keys for incoming authentication */
/*! Private key for outgoing authentication */
/*! Encryption AES-128 Key */
Mark Spencer
committed
aes_encrypt_ctx ecx;
/*! Decryption AES-128 Key */
Mark Spencer
committed
aes_decrypt_ctx dcx;
/*! 32 bytes of semi-random data */
Kevin P. Fleming
committed
unsigned char semirand[32];
/*! Preferred language */
char language[MAX_LANGUAGE];
/*! Hostname/peername for naming purposes */
/*! Associated registry */
/*! Associated peer for poking */
/*! IAX_ flags */
Mark Spencer
committed
/*! Transferring status */
enum iax_transfer_state transferring;
/*! Transfer identifier */
int transferid;
/*! Who we are IAX transfering to */
/*! What's the new call number for the transfer */
/*! Transfer decrypt AES-128 Key */
Mark Spencer
committed
aes_encrypt_ctx tdcx;
/*! Status of knowledge of peer ADSI capability */
/*! Who we are bridged to */
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 accountcode[AST_MAX_ACCOUNT_CODE];
int amaflags;
struct iax2_dpcache *dpentries;
/*! last received remote rr */
struct iax_rr remote_rr;
/*! Current base time: (just for stats) */
/*! Dropped frame count: (just for stats) */
int frames_dropped;
/*! received frame count: (just for stats) */
int frames_received;
};
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;
/*! Extension exists */
/*! Extension is nonexistent */
/*! Extension can exist */
/*! Waiting to hear back response */
/*! Timed out */
/*! Request transmitted */
/*! Timeout */
/*! Matchmore */
char peercontext[AST_MAX_CONTEXT];
char exten[AST_MAX_EXTENSION];
struct timeval orig;
Kevin P. Fleming
committed
struct timeval expiry;
int flags;
unsigned short callno;
int waiters[256];
struct iax2_dpcache *next;
struct iax2_dpcache *peer; /*!< For linking in peers */
Mark Spencer
committed
AST_MUTEX_DEFINE_STATIC(dpcache_lock);
Kevin P. Fleming
committed
static void reg_source_db(struct iax2_peer *p);
static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in *sin);
Kevin P. Fleming
committed
static void destroy_peer(struct iax2_peer *peer);
static int ast_cli_netstats(struct mansession *s, int fd, int limit_fmt);
#define IAX_IOSTATE_IDLE 0
#define IAX_IOSTATE_READY 1
#define IAX_IOSTATE_PROCESSING 2
#define IAX_IOSTATE_SCHEDREADY 3
struct iax2_thread {
ASTOBJ_COMPONENTS(struct iax2_thread);
int iostate;
#ifdef SCHED_MULTITHREADED
void (*schedfunc)(void *);
void *scheddata;
#endif
#ifdef DEBUG_SCHED_MULTITHREAD
char curfunc[80];
#endif
int actions;
int halt;
pthread_t threadid;
int threadnum;
struct sockaddr_in iosin;
unsigned char buf[4096];
int iores;
int iofd;
time_t checktime;
Kevin P. Fleming
committed
ast_mutex_t lock;
ast_cond_t cond;
};
struct iax2_thread_list {
ASTOBJ_CONTAINER_COMPONENTS(struct iax2_thread);
};
static struct iax2_thread_list idlelist, activelist;
Kevin P. Fleming
committed
static void signal_condition(ast_mutex_t *lock, ast_cond_t *cond)
{
ast_mutex_lock(lock);
ast_cond_signal(cond);
ast_mutex_unlock(lock);
}
static void iax_debug_output(const char *data)
static void iax_error_output(const char *data)
Mark Spencer
committed
731
732
733
734
735
736
737
738
739
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
#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];
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];
Kevin P. Fleming
committed
static int send_command(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
static int send_command_locked(unsigned short callno, char, int, unsigned int, const unsigned char *, int, int);
static int send_command_immediate(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
static int send_command_final(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
static int send_command_transfer(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int);
static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, int temponly);
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, int sockfd, 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);
Mark Spencer
committed
static int iax2_sendtext(struct ast_channel *c, const char *text);
static int iax2_sendimage(struct ast_channel *c, struct ast_frame *img);
Mark Spencer
committed
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 enum ast_bridge_result iax2_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms);
Mark Spencer
committed
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 = {
.description = tdesc,
.capabilities = IAX_CAPABILITY_FULLBANDWIDTH,
Mark Spencer
committed
.properties = AST_CHAN_TP_WANTSJITTER,
.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 struct iax2_thread *find_idle_thread(void)
{
struct iax2_thread *thread;
thread = ASTOBJ_CONTAINER_UNLINK_START(&idlelist);
return thread;
}
#ifdef SCHED_MULTITHREADED
static int __schedule_action(void (*func)(void *data), void *data, const char *funcname)
{
struct iax2_thread *thread;
static time_t lasterror;
static time_t t;
thread = find_idle_thread();
if (thread) {
thread->schedfunc = func;
thread->scheddata = data;
thread->iostate = IAX_IOSTATE_SCHEDREADY;
#ifdef DEBUG_SCHED_MULTITHREAD
ast_copy_string(thread->curfunc, funcname, sizeof(thread->curfunc));
#endif
Kevin P. Fleming
committed
signal_condition(&thread->lock, &thread->cond);
return 0;
}
time(&t);
if (t != lasterror)
ast_log(LOG_NOTICE, "Out of idle IAX2 threads for scheduling!\n");
lasterror = t;
return -1;
}
#define schedule_action(func, data) __schedule_action(func, data, __PRETTY_FUNCTION__)
#endif
static void __send_ping(void *data)
{
int callno = (long)data;
send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_PING, 0, NULL, 0, -1);
}
static int send_ping(void *data)
{
int callno = (long)data;
if (iaxs[callno]) {
#ifdef BRIDGE_OPTIMIZATION
{
#ifdef SCHED_MULTITHREADED
if (schedule_action(__send_ping, data))
#endif
__send_ping(data);
}
Mark Spencer
committed
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;
}
{
int callno = (long)data;
/* Ping only if it's real not if it's bridged */
send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_LAGRQ, 0, NULL, 0, -1);
}
static int send_lagrq(void *data)
{
int callno = (long)data;
if (iaxs[callno]) {
#ifdef BRIDGE_OPTIMIZATION
if (!iaxs[callno]->bridgecallno)
#endif
{
#ifdef SCHED_MULTITHREADED
if (schedule_action(__send_lagrq, data))
}
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 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);
peer = realtime_peer(name, NULL);
return peer;
}
static int iax2_getpeername(struct sockaddr_in sin, char *host, int len, int lockpeer)
{
struct iax2_peer *peer;
int res = 0;
ast_mutex_lock(&peerl.lock);
if ((peer->addr.sin_addr.s_addr == sin.sin_addr.s_addr) &&
(peer->addr.sin_port == sin.sin_port)) {
Kevin P. Fleming
committed
ast_copy_string(host, peer->name, len);
res = 1;
break;
}
peer = peer->next;
}
if (lockpeer)
ast_mutex_unlock(&peerl.lock);
if (!peer) {
peer = realtime_peer(NULL, &sin);
if (peer) {
ast_copy_string(host, peer->name, len);
if (ast_test_flag(peer, IAX_TEMPONLY))