/*
* 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.
*/
/*! \file
*
* \brief Implementation of Inter-Asterisk eXchange Version 2
* as specified in RFC 5456
*
* \author Mark Spencer <markster@digium.com>
*
* \par See also
* \arg \ref Config_iax
*
* \ingroup channel_drivers
*
* \todo Implement musicclass settings for IAX2 devices
*/
/*** MODULEINFO
<use>crypto</use>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <sys/mman.h>
#include <dirent.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <signal.h>
#include <strings.h>
#include <netdb.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <regex.h>
#include "asterisk/paths.h" /* need ast_config_AST_DATA_DIR for firmware */
#include "asterisk/lock.h"
#include "asterisk/frame.h"
#include "asterisk/channel.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/sched.h"
#include "asterisk/io.h"
#include "asterisk/config.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/dnsmgr.h"
#include "asterisk/devicestate.h"
#include "asterisk/netsock.h"
#include "asterisk/stringfields.h"
#include "asterisk/linkedlists.h"
#include "asterisk/event.h"
#include "asterisk/astobj2.h"
#include "asterisk/timing.h"
#include "asterisk/taskprocessor.h"
#include "asterisk/test.h"
#include "asterisk/data.h"
#include "asterisk/netsock2.h"
#include "iax2.h"
#include "iax2-parser.h"
#include "iax2-provision.h"
#include "jitterbuf.h"
/*** DOCUMENTATION
<application name="IAX2Provision" language="en_US">
<synopsis>
Provision a calling IAXy with a given template.
</synopsis>
<syntax>
<parameter name="template">
<para>If not specified, defaults to <literal>default</literal>.</para>
</parameter>
</syntax>
<description>
<para>Provisions the calling IAXy (assuming the calling entity is in fact an IAXy) with the
given <replaceable>template</replaceable>. Returns <literal>-1</literal> on error
or <literal>0</literal> on success.</para>
</description>
</application>
<function name="IAXPEER" language="en_US">
<synopsis>
Gets IAX peer information.
</synopsis>
<syntax>
<parameter name="peername" required="true">
<enumlist>
<enum name="CURRENTCHANNEL">
<para>If <replaceable>peername</replaceable> is specified to this value, return the IP address of the
endpoint of the current channel</para>
</enum>
</enumlist>
</parameter>
<parameter name="item">
<para>If <replaceable>peername</replaceable> is specified, valid items are:</para>
<enumlist>
<enum name="ip">
<para>(default) The IP address.</para>
</enum>
<enum name="status">
<para>The peer's status (if <literal>qualify=yes</literal>)</para>
</enum>
<enum name="mailbox">
<para>The configured mailbox.</para>
</enum>
<enum name="context">
<para>The configured context.</para>
</enum>
<enum name="expire">
<para>The epoch time of the next expire.</para>
</enum>
<enum name="dynamic">
<para>Is it dynamic? (yes/no).</para>
</enum>
<enum name="callerid_name">
<para>The configured Caller ID name.</para>
</enum>
<enum name="callerid_num">
<para>The configured Caller ID number.</para>
</enum>
<enum name="codecs">
<para>The configured codecs.</para>
</enum>
<enum name="codec[x]">
<para>Preferred codec index number <replaceable>x</replaceable> (beginning
with <literal>0</literal>)</para>
</enum>
</enumlist>
</parameter>
</syntax>
<description></description>
<see-also>
<ref type="function">SIPPEER</ref>
</see-also>
</function>
<function name="IAXVAR" language="en_US">
<synopsis>
Sets or retrieves a remote variable.
</synopsis>
<syntax>
<parameter name="varname" required="true" />
</syntax>
<description></description>
</function>
<manager name="IAXpeers" language="en_US">
<synopsis>
List IAX peers.
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
</syntax>
<description>
</description>
</manager>
<manager name="IAXpeerlist" language="en_US">
<synopsis>
List IAX Peers.
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
</syntax>
<description>
<para>List all the IAX peers.</para>
</description>
</manager>
<manager name="IAXnetstats" language="en_US">
<synopsis>
Show IAX Netstats.
</synopsis>
<syntax />
<description>
<para>Show IAX channels network statistics.</para>
</description>
</manager>
<manager name="IAXregistry" language="en_US">
<synopsis>
Show IAX registrations.
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
</syntax>
<description>
<para>Show IAX registrations.</para>
</description>
</manager>
***/
/* Define SCHED_MULTITHREADED to run the scheduler in a special
multithreaded mode. */
#define SCHED_MULTITHREADED
/* Define DEBUG_SCHED_MULTITHREADED to keep track of where each
thread is actually doing. */
#define DEBUG_SCHED_MULTITHREAD
#ifdef SO_NO_CHECK
static int nochecksums = 0;
#endif
#define PTR_TO_CALLNO(a) ((unsigned short)(unsigned long)(a))
#define CALLNO_TO_PTR(a) ((void *)(unsigned long)(a))
#define DEFAULT_THREAD_COUNT 10
#define DEFAULT_MAX_THREAD_COUNT 100
#define DEFAULT_RETRY_TIME 1000
#define MEMORY_SIZE 100
#define DEFAULT_DROP 3
#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 tdesc[] = "Inter Asterisk eXchange Driver (Ver 2)";
/*! \brief Maximum transmission unit for the UDP packet in the trunk not to be
fragmented. This is based on 1516 - ethernet - ip - udp - iax minus one g711 frame = 1240 */
#define MAX_TRUNK_MTU 1240
static int global_max_trunk_mtu; /*!< Maximum MTU, 0 if not used */
static int trunk_timed, trunk_untimed, trunk_maxmtu, trunk_nmaxmtu ; /*!< Trunk MTU statistics */
#define DEFAULT_CONTEXT "default"
static char default_parkinglot[AST_MAX_CONTEXT];
static char language[MAX_LANGUAGE] = "";
static char regcontext[AST_MAX_CONTEXT] = "";
static struct ast_event_sub *network_change_event_subscription; /*!< subscription id for network change events */
static int network_change_event_sched_id = -1;
static int maxauthreq = 3;
static int max_retries = 4;
static int ping_time = 21;
static int lagrq_time = 10;
static int maxjitterbuffer=1000;
static int resyncthreshold=1000;
static int maxjitterinterps=10;
static int jittertargetextra = 40; /* number of milliseconds the new jitter buffer adds on to its size */
#define MAX_TRUNKDATA 640 * 200 /*!< 40ms, uncompressed linear * 200 channels */
static int trunkfreq = 20;
static int trunkmaxsize = MAX_TRUNKDATA;
static int authdebug = 1;
static int autokill = 0;
static int iaxcompat = 0;
static int last_authmethod = 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 struct {
unsigned int tos;
unsigned int cos;
} qos = { 0, 0 };
static int min_reg_expire;
static int max_reg_expire;
static int srvlookup = 0;
static struct ast_timer *timer; /* Timer for trunking */
static struct ast_netsock_list *netsock;
static struct ast_netsock_list *outsock; /*!< used if sourceaddress specified and bindaddr == INADDR_ANY */
static int defaultsockfd = -1;
static int (*iax2_regfunk)(const char *username, int onoff) = NULL;
/* Ethernet, etc */
#define IAX_CAPABILITY_FULLBANDWIDTH 0xFFFF
/* T1, maybe ISDN */
#define IAX_CAPABILITY_MEDBANDWIDTH (IAX_CAPABILITY_FULLBANDWIDTH & \
~ast_format_id_to_old_bitfield(AST_FORMAT_SLINEAR) & \
~ast_format_id_to_old_bitfield(AST_FORMAT_SLINEAR16) & \
~ast_format_id_to_old_bitfield(AST_FORMAT_SIREN7) & \
~ast_format_id_to_old_bitfield(AST_FORMAT_SIREN14) & \
~ast_format_id_to_old_bitfield(AST_FORMAT_G719) & \
~ast_format_id_to_old_bitfield(AST_FORMAT_ULAW) & \
~ast_format_id_to_old_bitfield(AST_FORMAT_ALAW) & \
~ast_format_id_to_old_bitfield(AST_FORMAT_G722))
/* A modem */
#define IAX_CAPABILITY_LOWBANDWIDTH (IAX_CAPABILITY_MEDBANDWIDTH & \
~ast_format_id_to_old_bitfield(AST_FORMAT_G726) & \
~ast_format_id_to_old_bitfield(AST_FORMAT_G726_AAL2) & \
~ast_format_id_to_old_bitfield(AST_FORMAT_ADPCM))
#define IAX_CAPABILITY_LOWFREE (IAX_CAPABILITY_LOWBANDWIDTH & \
~ast_format_id_to_old_bitfield(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... */
/* if a pvt has encryption setup done and is running on the call */
#define IAX_CALLENCRYPTED(pvt) \
(ast_test_flag64(pvt, IAX_ENCRYPTED) && ast_test_flag64(pvt, IAX_KEYPOPULATED))
#define IAX_DEBUGDIGEST(msg, key) do { \
int idx; \
char digest[33] = ""; \
\
if (!iaxdebug) \
break; \
\
for (idx = 0; idx < 16; idx++) \
sprintf(digest + (idx << 1), "%2.2x", (unsigned char) key[idx]); \
\
ast_log(LOG_NOTICE, msg " IAX_COMMAND_RTKEY to rotate key to '%s'\n", digest); \
} while(0)
static struct io_context *io;
static struct ast_sched_context *sched;
#define DONT_RESCHEDULE -2
static iax2_format iax2_capability = IAX_CAPABILITY_FULLBANDWIDTH;
static int iaxdebug = 0;
static int iaxtrunkdebug = 0;
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 */
static char accountcode[AST_MAX_ACCOUNT_CODE];
static char mohinterpret[MAX_MUSICCLASS];
static char mohsuggest[MAX_MUSICCLASS];
static int amaflags = 0;
static int adsi = 0;
static int delayreject = 0;
static int iax2_encryption = 0;
static struct ast_flags64 globalflags = { 0 };
static pthread_t netthreadid = AST_PTHREADT_NULL;
enum iax2_state {
IAX_STATE_STARTED = (1 << 0),
IAX_STATE_AUTHENTICATED = (1 << 1),
IAX_STATE_TBD = (1 << 2),
};
struct iax2_context {
char context[AST_MAX_CONTEXT];
struct iax2_context *next;
};
#define IAX_HASCALLERID (uint64_t)(1 << 0) /*!< CallerID has been specified */
#define IAX_DELME (uint64_t)(1 << 1) /*!< Needs to be deleted */
#define IAX_TEMPONLY (uint64_t)(1 << 2) /*!< Temporary (realtime) */
#define IAX_TRUNK (uint64_t)(1 << 3) /*!< Treat as a trunk */
#define IAX_NOTRANSFER (uint64_t)(1 << 4) /*!< Don't native bridge */
#define IAX_USEJITTERBUF (uint64_t)(1 << 5) /*!< Use jitter buffer */
#define IAX_DYNAMIC (uint64_t)(1 << 6) /*!< dynamic peer */
#define IAX_SENDANI (uint64_t)(1 << 7) /*!< Send ANI along with CallerID */
#define IAX_RTSAVE_SYSNAME (uint64_t)(1 << 8) /*!< Save Systname on Realtime Updates */
#define IAX_ALREADYGONE (uint64_t)(1 << 9) /*!< Already disconnected */
#define IAX_PROVISION (uint64_t)(1 << 10) /*!< This is a provisioning request */
#define IAX_QUELCH (uint64_t)(1 << 11) /*!< Whether or not we quelch audio */
#define IAX_ENCRYPTED (uint64_t)(1 << 12) /*!< Whether we should assume encrypted tx/rx */
#define IAX_KEYPOPULATED (uint64_t)(1 << 13) /*!< Whether we have a key populated */
#define IAX_CODEC_USER_FIRST (uint64_t)(1 << 14) /*!< are we willing to let the other guy choose the codec? */
#define IAX_CODEC_NOPREFS (uint64_t)(1 << 15) /*!< Force old behaviour by turning off prefs */
#define IAX_CODEC_NOCAP (uint64_t)(1 << 16) /*!< only consider requested format and ignore capabilities*/
#define IAX_RTCACHEFRIENDS (uint64_t)(1 << 17) /*!< let realtime stay till your reload */
#define IAX_RTUPDATE (uint64_t)(1 << 18) /*!< Send a realtime update */
#define IAX_RTAUTOCLEAR (uint64_t)(1 << 19) /*!< erase me on expire */
#define IAX_FORCEJITTERBUF (uint64_t)(1 << 20) /*!< Force jitterbuffer, even when bridged to a channel that can take jitter */
#define IAX_RTIGNOREREGEXPIRE (uint64_t)(1 << 21) /*!< When using realtime, ignore registration expiration */
#define IAX_TRUNKTIMESTAMPS (uint64_t)(1 << 22) /*!< Send trunk timestamps */
#define IAX_TRANSFERMEDIA (uint64_t)(1 << 23) /*!< When doing IAX2 transfers, transfer media only */
#define IAX_MAXAUTHREQ (uint64_t)(1 << 24) /*!< Maximum outstanding AUTHREQ restriction is in place */
#define IAX_DELAYPBXSTART (uint64_t)(1 << 25) /*!< Don't start a PBX on the channel until the peer sends us a response, so that we've achieved a three-way handshake with them before sending voice or anything else */
#define IAX_ALLOWFWDOWNLOAD (uint64_t)(1 << 26) /*!< Allow the FWDOWNL command? */
#define IAX_IMMEDIATE (uint64_t)(1 << 27) /*!< Allow immediate off-hook to extension s */
#define IAX_SENDCONNECTEDLINE (uint64_t)(1 << 28) /*!< Allow sending of connected line updates */
#define IAX_RECVCONNECTEDLINE (uint64_t)(1 << 29) /*!< Allow receiving of connected line updates */
#define IAX_FORCE_ENCRYPT (uint64_t)(1 << 30) /*!< Forces call encryption, if encryption not possible hangup */
#define IAX_SHRINKCALLERID (uint64_t)(1 << 31) /*!< Turn on and off caller id shrinking */
static int global_rtautoclear = 120;
static int reload_config(void);
/*!
* \brief Call token validation settings.
*/
enum calltoken_peer_enum {
/*! \brief Default calltoken required unless the ip is in the ignorelist */
CALLTOKEN_DEFAULT = 0,
/*! \brief Require call token validation. */
CALLTOKEN_YES = 1,
/*! \brief Require call token validation after a successful registration
* using call token validation occurs. */
CALLTOKEN_AUTO = 2,
/*! \brief Do not require call token validation. */
CALLTOKEN_NO = 3,
};
struct iax2_user {
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(name);
AST_STRING_FIELD(secret);
AST_STRING_FIELD(dbsecret);
AST_STRING_FIELD(accountcode);
AST_STRING_FIELD(mohinterpret);
AST_STRING_FIELD(mohsuggest);
AST_STRING_FIELD(inkeys); /*!< Key(s) this user can use to authenticate to us */
AST_STRING_FIELD(language);
AST_STRING_FIELD(cid_num);
AST_STRING_FIELD(cid_name);
AST_STRING_FIELD(parkinglot); /*!< Default parkinglot for device */
);
int authmethods;
int encmethods;
int amaflags;
int adsi;
uint64_t flags;
iax2_format capability;
int maxauthreq; /*!< Maximum allowed outstanding AUTHREQs */
int curauthreq; /*!< Current number of outstanding AUTHREQs */
struct ast_codec_pref prefs;
struct ast_ha *ha;
struct iax2_context *contexts;
struct ast_variable *vars;
enum calltoken_peer_enum calltoken_required; /*!< Is calltoken validation required or not, can be YES, NO, or AUTO */
};
struct iax2_peer {
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(name);
AST_STRING_FIELD(username);
AST_STRING_FIELD(description); /*!< Description of the peer */
AST_STRING_FIELD(secret);
AST_STRING_FIELD(dbsecret);
AST_STRING_FIELD(outkey); /*!< What key we use to talk to this peer */
AST_STRING_FIELD(regexten); /*!< Extension to register (if regcontext is used) */
AST_STRING_FIELD(context); /*!< For transfers only */
AST_STRING_FIELD(peercontext); /*!< Context to pass to peer */
AST_STRING_FIELD(mailbox); /*!< Mailbox */
AST_STRING_FIELD(mohinterpret);
AST_STRING_FIELD(mohsuggest);
AST_STRING_FIELD(inkeys); /*!< Key(s) this peer can use to authenticate to us */
/* Suggested caller id if registering */
AST_STRING_FIELD(cid_num); /*!< Default context (for transfer really) */
AST_STRING_FIELD(cid_name); /*!< Default context (for transfer really) */
AST_STRING_FIELD(zonetag); /*!< Time Zone */
AST_STRING_FIELD(parkinglot); /*!< Default parkinglot for device */
);
struct ast_codec_pref prefs;
struct ast_dnsmgr_entry *dnsmgr; /*!< DNS refresh manager */
struct ast_sockaddr addr;
int formats;
int sockfd; /*!< Socket to use for transmission */
struct in_addr mask;
int adsi;
uint64_t 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_*) */
int expire; /*!< Schedule entry for expiry */
int expiry; /*!< How soon to expire */
iax2_format capability; /*!< Capability */
/* Qualification */
int callno; /*!< Call number of POKE request */
int pokeexpire; /*!< Scheduled qualification-related task (ie iax2_poke_peer_s or iax2_poke_noanswer) */
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 */
uint16_t maxcallno; /*!< Max call number limit for this peer. Set on registration */
struct ast_event_sub *mwi_event_sub;
struct ast_ha *ha;
enum calltoken_peer_enum calltoken_required; /*!< Is calltoken validation required or not, can be YES, NO, or AUTO */
};
#define IAX2_TRUNK_PREFACE (sizeof(struct iax_frame) + sizeof(struct ast_iax2_meta_hdr) + sizeof(struct ast_iax2_meta_trunk_hdr))
struct iax2_trunk_peer {
ast_mutex_t lock;
int sockfd;
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;
int trunkmaxmtu;
int trunkerror;
int calls;
AST_LIST_ENTRY(iax2_trunk_peer) list;
};
static AST_LIST_HEAD_STATIC(tpeers, iax2_trunk_peer);
struct iax_firmware {
AST_LIST_ENTRY(iax_firmware) list;
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,
TRANSFER_MBEGIN,
TRANSFER_MREADY,
TRANSFER_MRELEASED,
TRANSFER_MPASSTHROUGH,
TRANSFER_MEDIA,
TRANSFER_MEDIAPASS
};
struct iax2_registry {
struct ast_sockaddr addr; /*!< Who we connect to for registration purposes */
char username[80];
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, low 8 bits = new, high 8 bits = old */
int callno; /*!< Associated call number if applicable */
struct sockaddr_in us; /*!< Who the server thinks we are */
struct ast_dnsmgr_entry *dnsmgr; /*!< DNS refresh manager */
AST_LIST_ENTRY(iax2_registry) entry;
};
static AST_LIST_HEAD_STATIC(registrations, iax2_registry);
/* 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_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
/* used for first_iax_message and last_iax_message. If this bit is set it was TX, else RX */
#define MARK_IAX_SUBCLASS_TX 0x8000
static int iaxthreadcount = DEFAULT_THREAD_COUNT;
static int iaxmaxthreadcount = DEFAULT_MAX_THREAD_COUNT;
static int iaxdynamicthreadcount = 0;
static int iaxdynamicthreadnum = 0;
static int iaxactivethreadcount = 0;
struct iax_rr {
int jitter;
int losspct;
int losscnt;
int packets;
int delay;
int dropped;
int ooo;
};
struct iax2_pvt_ref;
struct chan_iax2_pvt {
/*! Socket to send/receive on for this call */
int sockfd;
/*! Last received voice format */
iax2_format voiceformat;
/*! Last received video format */
iax2_format videoformat;
/*! Last sent voice format */
iax2_format svoiceformat;
/*! Last sent video format */
iax2_format svideoformat;
/*! What we are capable of sending */
iax2_format capability;
/*! Last received timestamp */
unsigned int last;
/*! Last sent timestamp - never send the same timestamp twice in a single call */
unsigned int lastsent;
/*! Timestamp of the last video frame sent */
unsigned int lastvsent;
/*! Next outgoing timestamp if everything is good */
unsigned int nextpred;
/*! iax frame subclass that began iax2_pvt entry. 0x8000 bit is set on TX */
int first_iax_message;
/*! Last iax frame subclass sent or received for a iax2_pvt. 0x8000 bit is set on TX */
int last_iax_message;
/*! True if the last voice we transmitted was not silence/CNG */
unsigned int notsilenttx:1;
/*! Ping time */
unsigned int pingtime;
/*! Max time for initial response */
int maxtime;
/*! Peer Address */
struct sockaddr_in addr;
/*! Actual used codec preferences */
struct ast_codec_pref prefs;
/*! Requested codec preferences */
struct ast_codec_pref rprefs;
/*! Our call number */
unsigned short callno;
/*! Our callno_entry entry */
struct callno_entry *callno_entry;
/*! Peer callno */
unsigned short peercallno;
/*! Negotiated format, this is only used to remember what format was
chosen for an unauthenticated call so that the channel can get
created later using the right format */
iax2_format chosenformat;
/*! Peer selected format */
iax2_format peerformat;
/*! Peer capability */
iax2_format peercapability;
/*! timeval that we base our transmission on */
struct timeval offset;
/*! timeval that we base our delivery on */
struct timeval rxcore;
/*! The jitterbuffer */
jitterbuf *jb;
/*! active jb read scheduler id */
int jbid;
/*! LAG */
int lag;
/*! Error, as discovered by the manager */
int error;
/*! Owner if we have one */
struct ast_channel *owner;
/*! What's our state? */
struct ast_flags state;
/*! Expiry (optional) */
int expiry;
/*! 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;
AST_DECLARE_STRING_FIELDS(
/*! Peer name */
AST_STRING_FIELD(peer);
/*! Default Context */
AST_STRING_FIELD(context);
/*! Caller ID if available */
AST_STRING_FIELD(cid_num);
AST_STRING_FIELD(cid_name);
/*! Hidden Caller ID (i.e. ANI) if appropriate */
AST_STRING_FIELD(ani);
/*! DNID */
AST_STRING_FIELD(dnid);
/*! RDNIS */
AST_STRING_FIELD(rdnis);
/*! Requested Extension */
AST_STRING_FIELD(exten);
/*! Expected Username */
AST_STRING_FIELD(username);
/*! Expected Secret */
AST_STRING_FIELD(secret);
/*! MD5 challenge */
AST_STRING_FIELD(challenge);
/*! Public keys permitted keys for incoming authentication */
AST_STRING_FIELD(inkeys);
/*! Private key for outgoing authentication */
AST_STRING_FIELD(outkey);
/*! Preferred language */
AST_STRING_FIELD(language);
/*! Hostname/peername for naming purposes */
AST_STRING_FIELD(host);
AST_STRING_FIELD(dproot);
AST_STRING_FIELD(accountcode);
AST_STRING_FIELD(mohinterpret);
AST_STRING_FIELD(mohsuggest);
/*! received OSP token */
AST_STRING_FIELD(osptoken);
/*! Default parkinglot */
AST_STRING_FIELD(parkinglot);
);
/*! AUTHREJ all AUTHREP frames */
int authrej;
/*! permitted authentication methods */
int authmethods;
/*! permitted encryption methods */
int encmethods;
/*! Encryption AES-128 Key */
ast_aes_encrypt_key ecx;
/*! Decryption AES-128 Key corresponding to ecx */
ast_aes_decrypt_key mydcx;
/*! Decryption AES-128 Key used to decrypt peer frames */
ast_aes_decrypt_key dcx;
/*! scheduler id associated with iax_key_rotate
* for encrypted calls*/
int keyrotateid;
/*! 32 bytes of semi-random data */
unsigned char semirand[32];
/*! Associated registry */
struct iax2_registry *reg;
/*! Associated peer for poking */
struct iax2_peer *peerpoke;
/*! IAX_ flags */
uint64_t flags;
int adsi;
/*! Transferring status */
enum iax_transfer_state transferring;
/*! Transfer identifier */
int transferid;
/*! Who we are IAX transferring to */
struct sockaddr_in transfer;
/*! What's the new call number for the transfer */
unsigned short transfercallno;
/*! Transfer encrypt AES-128 Key */
ast_aes_encrypt_key tdcx;
/*! Status of knowledge of peer ADSI capability */
int peeradsicpe;
/*! Who we are bridged to */
unsigned short bridgecallno;
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;
int amaflags;
AST_LIST_HEAD_NOLOCK(, iax2_dpcache) dpentries;
/*! variables inherited from the user definition */
struct ast_variable *vars;
/*! variables transmitted in a NEW packet */
struct ast_variable *iaxvars;
/*! 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;
/*! num bytes used for calltoken ie, even an empty ie should contain 2 */
unsigned char calltoken_ie_len;
/*! hold all signaling frames from the pbx thread until we have a destination callno */
char hold_signaling;
/*! frame queue for signaling frames from pbx thread waiting for destination callno */
AST_LIST_HEAD_NOLOCK(signaling_queue, signaling_queue_entry) signaling_queue;
};
struct signaling_queue_entry {
struct ast_frame f;
AST_LIST_ENTRY(signaling_queue_entry) next;
};
/*! table of available call numbers */
static struct ao2_container *callno_pool;
/*! table of available trunk call numbers */
static struct ao2_container *callno_pool_trunk;
static const unsigned int CALLNO_POOL_BUCKETS = 2699;
/*!
* \brief a list of frames that may need to be retransmitted
*
* \note The contents of this list do not need to be explicitly destroyed
* on module unload. This is because all active calls are destroyed, and
* all frames in this queue will get destroyed as a part of that process.
*
* \note Contents protected by the iaxsl[] locks
*/
static AST_LIST_HEAD_NOLOCK(, iax_frame) frame_queue[IAX_MAX_CALLS + 1];
static struct ast_taskprocessor *transmit_processor;
static int randomcalltokendata;
static const time_t MAX_CALLTOKEN_DELAY = 10;
/*!
* This module will get much higher performance when doing a lot of
* user and peer lookups if the number of buckets is increased from 1.
* However, to maintain old behavior for Asterisk 1.4, these are set to
* 1 by default. When using multiple buckets, search order through these
* containers is considered random, so you will not be able to depend on
* the order the entires are specified in iax.conf for matching order. */
#ifdef LOW_MEMORY
#define MAX_PEER_BUCKETS 17
#else
#define MAX_PEER_BUCKETS 563
#endif
static struct ao2_container *peers;
#define MAX_USER_BUCKETS MAX_PEER_BUCKETS
static struct ao2_container *users;
/*! Table containing peercnt objects for every ip address consuming a callno */
static struct ao2_container *peercnts;
/*! Table containing custom callno limit rules for a range of ip addresses. */
static struct ao2_container *callno_limits;
/*! Table containing ip addresses not requiring calltoken validation */
static struct ao2_container *calltoken_ignores;
static uint16_t DEFAULT_MAXCALLNO_LIMIT = 2048;
static uint16_t DEFAULT_MAXCALLNO_LIMIT_NONVAL = 8192;
static uint16_t global_maxcallno;
/*! Total num of call numbers allowed to be allocated without calltoken validation */
static uint16_t global_maxcallno_nonval;
static uint16_t total_nonval_callno_used = 0;
/*! peer connection private, keeps track of all the call numbers
* consumed by a single ip address */
struct peercnt {
/*! ip address consuming call numbers */
unsigned long addr;
/*! Number of call numbers currently used by this ip address */
uint16_t cur;
/*! Max call numbers allowed for this ip address */
uint16_t limit;
/*! Specifies whether limit is set by a registration or not, if so normal
* limit setting rules do not apply to this address. */
unsigned char reg;
};
/*! used by both callno_limits and calltoken_ignores containers */
struct addr_range {
/*! ip address range for custom callno limit rule */
struct ast_ha ha;
/*! callno limit for this ip address range, only used in callno_limits container */
uint16_t limit;
/*! delete me marker for reloads */
unsigned char delme;
};
struct callno_entry {
/*! callno used for this entry */
uint16_t callno;
/*! was this callno calltoken validated or not */
unsigned char validated;
};
static AST_LIST_HEAD_STATIC(firmwares, iax_firmware);
enum {
/*! Extension exists */
CACHE_FLAG_EXISTS = (1 << 0),
/*! Extension is nonexistent */
CACHE_FLAG_NONEXISTENT = (1 << 1),
/*! Extension can exist */
CACHE_FLAG_CANEXIST = (1 << 2),
/*! Waiting to hear back response */
CACHE_FLAG_PENDING = (1 << 3),
/*! Timed out */
CACHE_FLAG_TIMEOUT = (1 << 4),
/*! Request transmitted */
CACHE_FLAG_TRANSMITTED = (1 << 5),
/*! Timeout */
CACHE_FLAG_UNKNOWN = (1 << 6),
/*! Matchmore */
CACHE_FLAG_MATCHMORE = (1 << 7),
};
struct iax2_dpcache {
char peercontext[AST_MAX_CONTEXT];
char exten[AST_MAX_EXTENSION];
struct timeval orig;
struct timeval expiry;
int flags;
unsigned short callno;
int waiters[256];
AST_LIST_ENTRY(iax2_dpcache) cache_list;
AST_LIST_ENTRY(iax2_dpcache) peer_list;
};
static AST_LIST_HEAD_STATIC(dpcache, iax2_dpcache);
static void reg_source_db(struct iax2_peer *p);
static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in *sin);
static struct iax2_user *realtime_user(const char *username, struct sockaddr_in *sin);
static int ast_cli_netstats(struct mansession *s, int fd, int limit_fmt);
static char *complete_iax2_peers(const char *line, const char *word, int pos, int state, uint64_t flags);
static char *complete_iax2_unregister(const char *line, const char *word, int pos, int state);
enum iax2_thread_iostate {
IAX_IOSTATE_IDLE,
IAX_IOSTATE_READY,
IAX_IOSTATE_PROCESSING,
IAX_IOSTATE_SCHEDREADY,
};
enum iax2_thread_type {
IAX_THREAD_TYPE_POOL,
IAX_THREAD_TYPE_DYNAMIC,
};
struct iax2_pkt_buf {
AST_LIST_ENTRY(iax2_pkt_buf) entry;
size_t len;
unsigned char buf[1];
};
struct iax2_thread {
AST_LIST_ENTRY(iax2_thread) list;
enum iax2_thread_type type;
enum iax2_thread_iostate iostate;
#ifdef SCHED_MULTITHREADED
void (*schedfunc)(const void *);
const void *scheddata;
#endif
#ifdef DEBUG_SCHED_MULTITHREAD
char curfunc[80];
#endif
int actions;
pthread_t threadid;
int threadnum;
struct sockaddr_in iosin;
unsigned char readbuf[4096];
unsigned char *buf;
ssize_t buf_len;
size_t buf_size;
int iofd;
time_t checktime;
ast_mutex_t lock;
ast_cond_t cond;
ast_mutex_t init_lock;
ast_cond_t init_cond;
/*! if this thread is processing a full frame,
some information about that frame will be stored
here, so we can avoid dispatching any more full
frames for that callno to other threads */
struct {
unsigned short callno;
struct sockaddr_in sin;
unsigned char type;
unsigned char csub;
} ffinfo;
/*! Queued up full frames for processing. If more full frames arrive for
* a call which this thread is already processing a full frame for, they
* are queued up here. */
AST_LIST_HEAD_NOLOCK(, iax2_pkt_buf) full_frames;
unsigned char stop;
};
/* Thread lists */
static AST_LIST_HEAD_STATIC(idle_list, iax2_thread);
static AST_LIST_HEAD_STATIC(active_list, iax2_thread);
static AST_LIST_HEAD_STATIC(dynamic_list, iax2_thread);
static void *iax2_process_thread(void *data);
static void iax2_destroy(int callno);
static void signal_condition(ast_mutex_t *lock, ast_cond_t *cond)
{
ast_mutex_lock(lock);
ast_cond_signal(cond);
ast_mutex_unlock(lock);
}
/*!
* \brief an array of iax2 pvt structures
*
* The container for active chan_iax2_pvt structures is implemented as an
* array for extremely quick direct access to the correct pvt structure
* based on the local call number. The local call number is used as the
* index into the array where the associated pvt structure is stored.
*/
static struct chan_iax2_pvt *iaxs[IAX_MAX_CALLS + 1];
/*!
* \brief Another container of iax2_pvt structures
*
* Active IAX2 pvt structs are also stored in this container, if they are a part
* of an active call where we know the remote side's call number. The reason
* for this is that incoming media frames do not contain our call number. So,
* instead of having to iterate the entire iaxs array, we use this container to
* look up calls where the remote side is using a given call number.
*/
static struct ao2_container *iax_peercallno_pvts;
/*!
* \brief chan_iax2_pvt structure locks
*
* These locks are used when accessing a pvt structure in the iaxs array.
* The index used here is the same as used in the iaxs array. It is the
* local call number for the associated pvt struct.
*/
static ast_mutex_t iaxsl[ARRAY_LEN(iaxs)];
/*!
* * \brief Another container of iax2_pvt structures
*
* Active IAX2 pvt stucts used during transfering a call are stored here.
*/
static struct ao2_container *iax_transfercallno_pvts;
/* 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 IAX_MAX_CALLS / 2
/* Debug routines... */
static struct sockaddr_in debugaddr;
static void iax_outputframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen)
{
if (iaxdebug ||
(sin && debugaddr.sin_addr.s_addr &&
(!ntohs(debugaddr.sin_port) ||
debugaddr.sin_port == sin->sin_port) &&
debugaddr.sin_addr.s_addr == sin->sin_addr.s_addr)) {
if (iaxdebug) {
iax_showframe(f, fhi, rx, sin, datalen);
} else {
iaxdebug = 1;
iax_showframe(f, fhi, rx, sin, datalen);
iaxdebug = 0;
}
}
}
static void iax_debug_output(const char *data)
{
if (iaxdebug)
ast_verbose("%s", data);
}
static void iax_error_output(const char *data)
{
ast_log(LOG_WARNING, "%s", data);
}
static void __attribute__((format(printf, 1, 2))) jb_error_output(const char *fmt, ...)
{
va_list args;
char buf[1024];
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
ast_log(LOG_ERROR, "%s", buf);
}
static void __attribute__((format(printf, 1, 2))) jb_warning_output(const char *fmt, ...)
{
va_list args;
char buf[1024];
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
ast_log(LOG_WARNING, "%s", buf);
}
static void __attribute__((format(printf, 1, 2))) jb_debug_output(const char *fmt, ...)
{
va_list args;
char buf[1024];
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
ast_verbose("%s", buf);
}
static int maxtrunkcall = TRUNK_CALL_START;
static int maxnontrunkcall = 1;
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);
static int expire_registry(const void *data);
static int iax2_answer(struct ast_channel *c);
static int iax2_call(struct ast_channel *c, char *dest, int timeout);
static int iax2_devicestate(void *data);
static int iax2_digit_begin(struct ast_channel *c, char digit);
static int iax2_digit_end(struct ast_channel *c, char digit, unsigned int duration);
static int iax2_do_register(struct iax2_registry *reg);
static int iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan);
static int iax2_hangup(struct ast_channel *c);
static int iax2_indicate(struct ast_channel *c, int condition, const void *data, size_t datalen);
static int iax2_poke_peer(struct iax2_peer *peer, int heldcall);
static int iax2_provision(struct sockaddr_in *end, int sockfd, const char *dest, const char *template, int force);
static int iax2_send(struct chan_iax2_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno, int now, int transfer, int final);
static int iax2_sendhtml(struct ast_channel *c, int subclass, const char *data, int datalen);
static int iax2_sendimage(struct ast_channel *c, struct ast_frame *img);
static int iax2_sendtext(struct ast_channel *c, const char *text);
static int iax2_setoption(struct ast_channel *c, int option, void *data, int datalen);
static int iax2_queryoption(struct ast_channel *c, int option, void *data, int *datalen);
static int iax2_transfer(struct ast_channel *c, const char *dest);
static int iax2_write(struct ast_channel *c, struct ast_frame *f);
static int iax2_sched_add(struct ast_sched_context *sched, int when, ast_sched_cb callback, const void *data);
static int send_trunk(struct iax2_trunk_peer *tpeer, struct timeval *now);
static int send_command(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_immediate(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_transfer(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int);
static struct ast_channel *iax2_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, void *data, int *cause);
static struct ast_frame *iax2_read(struct ast_channel *c);
static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly);
static struct iax2_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly);
static void realtime_update_peer(const char *peername, struct ast_sockaddr *sockaddr, time_t regtime);
static void *iax2_dup_variable_datastore(void *);
static void prune_peers(void);
static void prune_users(void);
static void iax2_free_variable_datastore(void *);
static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen);
static int decode_frame(ast_aes_decrypt_key *dcx, struct ast_iax2_full_hdr *fh, struct ast_frame *f, int *datalen);
static int encrypt_frame(ast_aes_encrypt_key *ecx, struct ast_iax2_full_hdr *fh, unsigned char *poo, int *datalen);
static void build_ecx_key(const unsigned char *digest, struct chan_iax2_pvt *pvt);
static void build_rand_pad(unsigned char *buf, ssize_t len);
static struct callno_entry *get_unused_callno(int trunk, int validated);
static int replace_callno(const void *obj);
static void sched_delay_remove(struct sockaddr_in *sin, struct callno_entry *callno_entry);
static void network_change_event_cb(const struct ast_event *, void *);
static struct ast_channel_tech iax2_tech = {
.type = "IAX2",
.description = tdesc,
.properties = AST_CHAN_TP_WANTSJITTER,
.requester = iax2_request,
.devicestate = iax2_devicestate,
.send_digit_begin = iax2_digit_begin,
.send_digit_end = iax2_digit_end,
.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,
.queryoption = iax2_queryoption,
.bridge = iax2_bridge,
.transfer = iax2_transfer,
.fixup = iax2_fixup,
.func_channel_read = acf_channel_read,
};
/*!
* \internal
* \brief Obtain the owner channel lock if the owner exists.
*
* \param callno IAX2 call id.
*
* \note Assumes the iaxsl[callno] lock is already obtained.
*
* \note
* IMPORTANT NOTE!!! Any time this function is used, even if
* iaxs[callno] was valid before calling it, it may no longer be
* valid after calling it. This function may unlock and lock
* the mutex associated with this callno, meaning that another
* thread may grab it and destroy the call.
*
* \return Nothing
*/
static void iax2_lock_owner(int callno)
{
for (;;) {
if (!iaxs[callno] || !iaxs[callno]->owner) {
/* There is no owner lock to get. */
break;
}
if (!ast_channel_trylock(iaxs[callno]->owner)) {
/* We got the lock */
break;
}
/* Avoid deadlock by pausing and trying again */
DEADLOCK_AVOIDANCE(&iaxsl[callno]);
}
}
static void mwi_event_cb(const struct ast_event *event, void *userdata)
{
/* The MWI subscriptions exist just so the core knows we care about those
* mailboxes. However, we just grab the events out of the cache when it
* is time to send MWI, since it is only sent with a REGACK. */
}
static void network_change_event_subscribe(void)
{
if (!network_change_event_subscription) {
network_change_event_subscription = ast_event_subscribe(AST_EVENT_NETWORK_CHANGE,
network_change_event_cb, "IAX2 Network Change", NULL, AST_EVENT_IE_END);
}
}
static void network_change_event_unsubscribe(void)
{
if (network_change_event_subscription) {
network_change_event_subscription = ast_event_unsubscribe(network_change_event_subscription);
}
}
static int network_change_event_sched_cb(const void *data)
{
struct iax2_registry *reg;
network_change_event_sched_id = -1;
AST_LIST_LOCK(®istrations);
AST_LIST_TRAVERSE(®istrations, reg, entry) {
iax2_do_register(reg);
}
AST_LIST_UNLOCK(®istrations);
return 0;
}
static void network_change_event_cb(const struct ast_event *event, void *userdata)
{
ast_debug(1, "IAX, got a network change event, renewing all IAX registrations.\n");
if (network_change_event_sched_id == -1) {
network_change_event_sched_id = iax2_sched_add(sched, 1000, network_change_event_sched_cb, NULL);
}
}
/*! \brief Send manager event at call setup to link between Asterisk channel name
and IAX2 call identifiers */
static void iax2_ami_channelupdate(struct chan_iax2_pvt *pvt)
{
manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
"Channel: %s\r\nChanneltype: IAX2\r\nIAX2-callno-local: %d\r\nIAX2-callno-remote: %d\r\nIAX2-peer: %s\r\n",
pvt->owner ? pvt->owner->name : "",
pvt->callno, pvt->peercallno, pvt->peer ? pvt->peer : "");
}
static struct ast_datastore_info iax2_variable_datastore_info = {
.type = "IAX2_VARIABLE",
.duplicate = iax2_dup_variable_datastore,
.destroy = iax2_free_variable_datastore,
};
static void *iax2_dup_variable_datastore(void *old)
{
AST_LIST_HEAD(, ast_var_t) *oldlist = old, *newlist;
struct ast_var_t *oldvar, *newvar;
newlist = ast_calloc(sizeof(*newlist), 1);
if (!newlist) {
ast_log(LOG_ERROR, "Unable to duplicate iax2 variables\n");
return NULL;
}
AST_LIST_HEAD_INIT(newlist);
AST_LIST_LOCK(oldlist);
AST_LIST_TRAVERSE(oldlist, oldvar, entries) {
newvar = ast_var_assign(ast_var_name(oldvar), ast_var_value(oldvar));
if (newvar)
AST_LIST_INSERT_TAIL(newlist, newvar, entries);
else
ast_log(LOG_ERROR, "Unable to duplicate iax2 variable '%s'\n", ast_var_name(oldvar));
}
AST_LIST_UNLOCK(oldlist);
return newlist;
}
static void iax2_free_variable_datastore(void *old)
{
AST_LIST_HEAD(, ast_var_t) *oldlist = old;
struct ast_var_t *oldvar;
AST_LIST_LOCK(oldlist);
while ((oldvar = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
ast_free(oldvar);
}
AST_LIST_UNLOCK(oldlist);
AST_LIST_HEAD_DESTROY(oldlist);
ast_free(oldlist);
}
/* WARNING: insert_idle_thread should only ever be called within the
* context of an iax2_process_thread() thread.
*/
static void insert_idle_thread(struct iax2_thread *thread)
{
if (thread->type == IAX_THREAD_TYPE_DYNAMIC) {
AST_LIST_LOCK(&dynamic_list);
AST_LIST_INSERT_TAIL(&dynamic_list, thread, list);
AST_LIST_UNLOCK(&dynamic_list);
} else {
AST_LIST_LOCK(&idle_list);
AST_LIST_INSERT_TAIL(&idle_list, thread, list);
AST_LIST_UNLOCK(&idle_list);
}
return;
}
static struct iax2_thread *find_idle_thread(void)
{
struct iax2_thread *thread = NULL;
/* Pop the head of the idle list off */
AST_LIST_LOCK(&idle_list);
thread = AST_LIST_REMOVE_HEAD(&idle_list, list);
AST_LIST_UNLOCK(&idle_list);
/* If we popped a thread off the idle list, just return it */
if (thread) {
memset(&thread->ffinfo, 0, sizeof(thread->ffinfo));
return thread;
}
/* Pop the head of the dynamic list off */
AST_LIST_LOCK(&dynamic_list);
thread = AST_LIST_REMOVE_HEAD(&dynamic_list, list);
AST_LIST_UNLOCK(&dynamic_list);
/* If we popped a thread off the dynamic list, just return it */
if (thread) {
memset(&thread->ffinfo, 0, sizeof(thread->ffinfo));
return thread;
}
/* If we can't create a new dynamic thread for any reason, return no thread at all */
if (iaxdynamicthreadcount >= iaxmaxthreadcount || !(thread = ast_calloc(1, sizeof(*thread))))
return NULL;
/* Set default values */
ast_atomic_fetchadd_int(&iaxdynamicthreadcount, 1);
thread->threadnum = ast_atomic_fetchadd_int(&iaxdynamicthreadnum, 1);
thread->type = IAX_THREAD_TYPE_DYNAMIC;
/* Initialize lock and condition */
ast_mutex_init(&thread->lock);
ast_cond_init(&thread->cond, NULL);
ast_mutex_init(&thread->init_lock);
ast_cond_init(&thread->init_cond, NULL);
ast_mutex_lock(&thread->init_lock);
/* Create thread and send it on it's way */
if (ast_pthread_create_background(&thread->threadid, NULL, iax2_process_thread, thread)) {
ast_cond_destroy(&thread->cond);
ast_mutex_destroy(&thread->lock);
ast_mutex_unlock(&thread->init_lock);
ast_cond_destroy(&thread->init_cond);
ast_mutex_destroy(&thread->init_lock);
ast_free(thread);
return NULL;
}
/* this thread is not processing a full frame (since it is idle),
so ensure that the field for the full frame call number is empty */
memset(&thread->ffinfo, 0, sizeof(thread->ffinfo));
/* Wait for the thread to be ready before returning it to the caller */
ast_cond_wait(&thread->init_cond, &thread->init_lock);
/* Done with init_lock */
ast_mutex_unlock(&thread->init_lock);
return thread;
}
#ifdef SCHED_MULTITHREADED
static int __schedule_action(void (*func)(const void *data), const void *data, const char *funcname)
{
struct iax2_thread *thread = NULL;
static time_t lasterror;
static time_t t;
thread = find_idle_thread();
if (thread != NULL) {
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
signal_condition(&thread->lock, &thread->cond);
return 0;
}
time(&t);
if (t != lasterror)
ast_debug(1, "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 int iax2_sched_replace(int id, struct ast_sched_context *con, int when,
ast_sched_cb callback, const void *data)
{
return ast_sched_replace(id, con, when, callback, data);
}
static int iax2_sched_add(struct ast_sched_context *con, int when,
ast_sched_cb callback, const void *data)
{
return ast_sched_add(con, when, callback, data);
}
static int send_ping(const void *data);
static void __send_ping(const void *data)
{
int callno = (long) data;
ast_mutex_lock(&iaxsl[callno]);
if (iaxs[callno]) {
if (iaxs[callno]->peercallno) {
send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_PING, 0, NULL, 0, -1);
if (iaxs[callno]->pingid != DONT_RESCHEDULE) {
iaxs[callno]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, data);
}
}
} else {
ast_debug(1, "I was supposed to send a PING with callno %d, but no such call exists.\n", callno);
}
ast_mutex_unlock(&iaxsl[callno]);
}
static int send_ping(const void *data)
{
int callno = (long) data;
ast_mutex_lock(&iaxsl[callno]);
if (iaxs[callno] && iaxs[callno]->pingid != DONT_RESCHEDULE) {
iaxs[callno]->pingid = -1;
}
ast_mutex_unlock(&iaxsl[callno]);
#ifdef SCHED_MULTITHREADED
if (schedule_action(__send_ping, data))
#endif
__send_ping(data);
return 0;
}
static void encmethods_to_str(int e, struct ast_str *buf)
{
ast_str_set(&buf, 0, "(");
if (e & IAX_ENCRYPT_AES128) {
ast_str_append(&buf, 0, "aes128");
}
if (e & IAX_ENCRYPT_KEYROTATE) {
ast_str_append(&buf, 0, ",keyrotate");
}
if (ast_str_strlen(buf) > 1) {
ast_str_append(&buf, 0, ")");
} else {
ast_str_set(&buf, 0, "No");
}
}
static int get_encrypt_methods(const char *s)
{
int e;
if (!strcasecmp(s, "aes128"))
e = IAX_ENCRYPT_AES128 | IAX_ENCRYPT_KEYROTATE;
else if (ast_true(s))
e = IAX_ENCRYPT_AES128 | IAX_ENCRYPT_KEYROTATE;
else
e = 0;
return e;
}
static int send_lagrq(const void *data);
static void __send_lagrq(const void *data)
{
int callno = (long) data;
ast_mutex_lock(&iaxsl[callno]);
if (iaxs[callno]) {
if (iaxs[callno]->peercallno) {
send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_LAGRQ, 0, NULL, 0, -1);
if (iaxs[callno]->lagid != DONT_RESCHEDULE) {
iaxs[callno]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, data);
}
}
} else {
ast_debug(1, "I was supposed to send a LAGRQ with callno %d, but no such call exists.\n", callno);
}
ast_mutex_unlock(&iaxsl[callno]);
}
static int send_lagrq(const void *data)
{
int callno = (long) data;
ast_mutex_lock(&iaxsl[callno]);
if (iaxs[callno] && iaxs[callno]->lagid != DONT_RESCHEDULE) {
iaxs[callno]->lagid = -1;
}
ast_mutex_unlock(&iaxsl[callno]);
#ifdef SCHED_MULTITHREADED
if (schedule_action(__send_lagrq, data))
#endif
__send_lagrq(data);
return 0;
}
static unsigned char compress_subclass(iax2_format subclass)
{
int x;
int power=-1;
/* If it's 64 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 & (1LL << x)) {
if (power > -1) {
ast_log(LOG_WARNING, "Can't compress subclass %lld\n", (long long) subclass);
return 0;
} else
power = x;
}
}
return power | IAX_FLAG_SC_LOG;
}
static iax2_format 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 1LL << (csub & ~IAX_FLAG_SC_LOG & IAX_MAX_SHIFT);
}
else
return csub;
}
static iax2_format iax2_codec_choose(struct ast_codec_pref *pref, iax2_format formats, int find_best)
{
struct ast_format_cap *cap;
struct ast_format tmpfmt;
iax2_format format = 0;
if ((cap = ast_format_cap_alloc_nolock())) {
ast_format_clear(&tmpfmt);
ast_format_cap_from_old_bitfield(cap, formats);
ast_codec_choose(pref, cap, find_best, &tmpfmt);
format = ast_format_to_old_bitfield(&tmpfmt);
cap = ast_format_cap_destroy(cap);
}
return format;
}
static iax2_format iax2_best_codec(iax2_format formats)
{
struct ast_format_cap *cap = ast_format_cap_alloc_nolock();
struct ast_format tmpfmt;
if (!cap) {
return 0;
}
ast_format_clear(&tmpfmt);
ast_format_cap_from_old_bitfield(cap, formats);
ast_best_codec(cap, &tmpfmt);
cap = ast_format_cap_destroy(cap);
return ast_format_to_old_bitfield(&tmpfmt);
}
const char *iax2_getformatname(iax2_format format)
{
struct ast_format tmpfmt;
if (!(ast_format_from_old_bitfield(&tmpfmt, format))) {
return "Unknown";
}
return ast_getformatname(&tmpfmt);
}
static char *iax2_getformatname_multiple(char *codec_buf, size_t len, iax2_format format)
{
struct ast_format_cap *cap = ast_format_cap_alloc_nolock();
if (!cap) {
return "(Nothing)";
}
ast_format_cap_from_old_bitfield(cap, format);
ast_getformatname_multiple(codec_buf, len, cap);
cap = ast_format_cap_destroy(cap);
return codec_buf;
}
static int iax2_parse_allow_disallow(struct ast_codec_pref *pref, iax2_format *formats, const char *list, int allowing)
{
int res;
struct ast_format_cap *cap = ast_format_cap_alloc_nolock();
if (!cap) {
return 1;
}
ast_format_cap_from_old_bitfield(cap, *formats);
res = ast_parse_allow_disallow(pref, cap, list, allowing);
*formats = ast_format_cap_to_old_bitfield(cap);
cap = ast_format_cap_destroy(cap);
return res;
}
static int iax2_data_add_codecs(struct ast_data *root, const char *node_name, iax2_format formats)
{
int res;
struct ast_format_cap *cap = ast_format_cap_alloc_nolock();
if (!cap) {
return -1;
}
ast_format_cap_from_old_bitfield(cap, formats);
res = ast_data_add_codecs(root, node_name, cap);
cap = ast_format_cap_destroy(cap);
return res;
}
/*!
* \note The only member of the peer passed here guaranteed to be set is the name field
*/
static int peer_hash_cb(const void *obj, const int flags)
{
const struct iax2_peer *peer = obj;
return ast_str_hash(peer->name);
}
/*!
* \note The only member of the peer passed here guaranteed to be set is the name field
*/
static int peer_cmp_cb(void *obj, void *arg, int flags)
{
struct iax2_peer *peer = obj, *peer2 = arg;
return !strcmp(peer->name, peer2->name) ? CMP_MATCH | CMP_STOP : 0;
}
/*!
* \note The only member of the user passed here guaranteed to be set is the name field
*/
static int user_hash_cb(const void *obj, const int flags)
{
const struct iax2_user *user = obj;
return ast_str_hash(user->name);
}
/*!
* \note The only member of the user passed here guaranteed to be set is the name field
*/
static int user_cmp_cb(void *obj, void *arg, int flags)
{
struct iax2_user *user = obj, *user2 = arg;
return !strcmp(user->name, user2->name) ? CMP_MATCH | CMP_STOP : 0;
}
/*!
* \note This funtion calls realtime_peer -> reg_source_db -> iax2_poke_peer -> find_callno,
* so do not call it with a pvt lock held.
*/
static struct iax2_peer *find_peer(const char *name, int realtime)
{
struct iax2_peer *peer = NULL;
struct iax2_peer tmp_peer = {
.name = name,
};
peer = ao2_find(peers, &tmp_peer, OBJ_POINTER);
/* Now go for realtime if applicable */
if(!peer && realtime)
peer = realtime_peer(name, NULL);
return peer;
}
static struct iax2_peer *peer_ref(struct iax2_peer *peer)
{
ao2_ref(peer, +1);
return peer;
}
static inline struct iax2_peer *peer_unref(struct iax2_peer *peer)
{
ao2_ref(peer, -1);
return NULL;
}
static struct iax2_user *find_user(const char *name)
{
struct iax2_user tmp_user = {
.name = name,
};
return ao2_find(users, &tmp_user, OBJ_POINTER);
}
static inline struct iax2_user *user_ref(struct iax2_user *user)
{
ao2_ref(user, +1);
return user;
}
static inline struct iax2_user *user_unref(struct iax2_user *user)
{
ao2_ref(user, -1);
return NULL;
}
static int iax2_getpeername(struct sockaddr_in sin, char *host, int len)
{
struct iax2_peer *peer = NULL;
int res = 0;
struct ao2_iterator i;
i = ao2_iterator_init(peers, 0);
while ((peer = ao2_iterator_next(&i))) {
struct sockaddr_in peer_addr;
ast_sockaddr_to_sin(&peer->addr, &peer_addr);
if ((peer_addr.sin_addr.s_addr == sin.sin_addr.s_addr) &&
(peer_addr.sin_port == sin.sin_port)) {
ast_copy_string(host, peer->name, len);
peer_unref(peer);
res = 1;
break;
}
peer_unref(peer);
}
ao2_iterator_destroy(&i);
if (!peer) {
peer = realtime_peer(NULL, &sin);
if (peer) {
ast_copy_string(host, peer->name, len);
peer_unref(peer);
res = 1;
}
}
return res;
}
/*!\note Assumes the lock on the pvt is already held, when
* iax2_destroy_helper() is called. */
static void iax2_destroy_helper(struct chan_iax2_pvt *pvt)
{
/* Decrement AUTHREQ count if needed */
if (ast_test_flag64(pvt, IAX_MAXAUTHREQ)) {
struct iax2_user *user;
struct iax2_user tmp_user = {
.name = pvt->username,
};
user = ao2_find(users, &tmp_user, OBJ_POINTER);
if (user) {
ast_atomic_fetchadd_int(&user->curauthreq, -1);
user_unref(user);
}
ast_clear_flag64(pvt, IAX_MAXAUTHREQ);
}
/* No more pings or lagrq's */
AST_SCHED_DEL_SPINLOCK(sched, pvt->pingid, &iaxsl[pvt->callno]);
pvt->pingid = DONT_RESCHEDULE;
AST_SCHED_DEL_SPINLOCK(sched, pvt->lagid, &iaxsl[pvt->callno]);
pvt->lagid = DONT_RESCHEDULE;
AST_SCHED_DEL(sched, pvt->autoid);
AST_SCHED_DEL(sched, pvt->authid);
AST_SCHED_DEL(sched, pvt->initid);
AST_SCHED_DEL(sched, pvt->jbid);
AST_SCHED_DEL(sched, pvt->keyrotateid);
}
static void iax2_frame_free(struct iax_frame *fr)
{
AST_SCHED_DEL(sched, fr->retrans);
iax_frame_free(fr);
}
static int scheduled_destroy(const void *vid)
{
unsigned short callno = PTR_TO_CALLNO(vid);
ast_mutex_lock(&iaxsl[callno]);
if (iaxs[callno]) {
ast_debug(1, "Really destroying %d now...\n", callno);
iax2_destroy(callno);
}
ast_mutex_unlock(&iaxsl[callno]);
return 0;
}
static void free_signaling_queue_entry(struct signaling_queue_entry *s)
{
ast_free(s->f.data.ptr);
ast_free(s);
}
/*! \brief This function must be called once we are sure the other side has
* given us a call number. All signaling is held here until that point. */
static void send_signaling(struct chan_iax2_pvt *pvt)
{
struct signaling_queue_entry *s = NULL;
while ((s = AST_LIST_REMOVE_HEAD(&pvt->signaling_queue, next))) {
iax2_send(pvt, &s->f, 0, -1, 0, 0, 0);
free_signaling_queue_entry(s);
}
pvt->hold_signaling = 0;
}
/*! \brief All frames other than that of type AST_FRAME_IAX must be held until
* we have received a destination call number. */
static int queue_signalling(struct chan_iax2_pvt *pvt, struct ast_frame *f)
{
struct signaling_queue_entry *new;
if (f->frametype == AST_FRAME_IAX || !pvt->hold_signaling) {
return 1; /* do not queue this frame */
} else if (!(new = ast_calloc(1, sizeof(struct signaling_queue_entry)))) {
return -1; /* out of memory */
}
memcpy(&new->f, f, sizeof(new->f)); /* copy ast_frame into our queue entry */
if (new->f.datalen) { /* if there is data in this frame copy it over as well */
if (!(new->f.data.ptr = ast_calloc(1, new->f.datalen))) {
free_signaling_queue_entry(new);
return -1;
}
memcpy(new->f.data.ptr, f->data.ptr, sizeof(*new->f.data.ptr));
}
AST_LIST_INSERT_TAIL(&pvt->signaling_queue, new, next);
return 0;
}
static void pvt_destructor(void *obj)
{
struct chan_iax2_pvt *pvt = obj;
struct iax_frame *cur = NULL;
struct signaling_queue_entry *s = NULL;
ast_mutex_lock(&iaxsl[pvt->callno]);
iax2_destroy_helper(pvt);
sched_delay_remove(&pvt->addr, pvt->callno_entry);
pvt->callno_entry = NULL;
/* Already gone */
ast_set_flag64(pvt, IAX_ALREADYGONE);
AST_LIST_TRAVERSE(&frame_queue[pvt->callno], cur, list) {
/* Cancel any pending transmissions */
cur->retries = -1;
}
ast_mutex_unlock(&iaxsl[pvt->callno]);
while ((s = AST_LIST_REMOVE_HEAD(&pvt->signaling_queue, next))) {
free_signaling_queue_entry(s);
}
if (pvt->reg) {
pvt->reg->callno = 0;
}
if (!pvt->owner) {
jb_frame frame;
if (pvt->vars) {
ast_variables_destroy(pvt->vars);
pvt->vars = NULL;
}
while (jb_getall(pvt->jb, &frame) == JB_OK) {
iax2_frame_free(frame.data);
}
jb_destroy(pvt->jb);
ast_string_field_free_memory(pvt);
}
}
static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, const char *host)
{
struct chan_iax2_pvt *tmp;
jb_conf jbconf;
if (!(tmp = ao2_alloc(sizeof(*tmp), pvt_destructor))) {
return NULL;
}
if (ast_string_field_init(tmp, 32)) {
ao2_ref(tmp, -1);
tmp = NULL;
return NULL;
}
tmp->prefs = prefs;
tmp->pingid = -1;
tmp->lagid = -1;
tmp->autoid = -1;
tmp->authid = -1;
tmp->initid = -1;
tmp->keyrotateid = -1;
ast_string_field_set(tmp,exten, "s");
ast_string_field_set(tmp,host, host);
tmp->jb = jb_new();
tmp->jbid = -1;
jbconf.max_jitterbuf = maxjitterbuffer;
jbconf.resync_threshold = resyncthreshold;
jbconf.max_contig_interp = maxjitterinterps;
jbconf.target_extra = jittertargetextra;
jb_setconf(tmp->jb,&jbconf);
AST_LIST_HEAD_INIT_NOLOCK(&tmp->dpentries);
tmp->hold_signaling = 1;
AST_LIST_HEAD_INIT_NOLOCK(&tmp->signaling_queue);
return tmp;
}
static struct iax_frame *iaxfrdup2(struct iax_frame *fr)
{
struct iax_frame *new = iax_frame_new(DIRECTION_INGRESS, fr->af.datalen, fr->cacheable);
if (new) {
size_t afdatalen = new->afdatalen;
memcpy(new, fr, sizeof(*new));
iax_frame_wrap(new, &fr->af);
new->afdatalen = afdatalen;
new->data = NULL;
new->datalen = 0;
new->direction = DIRECTION_INGRESS;
new->retrans = -1;
}
return new;
}
/* keep these defined in this order. They are used in find_callno to
* determine whether or not a new call number should be allowed. */
enum {
/* do not allow a new call number, only search ones in use for match */
NEW_PREVENT = 0,
/* search for match first, then allow a new one to be allocated */
NEW_ALLOW = 1,
/* do not search for match, force a new call number */
NEW_FORCE = 2,
/* do not search for match, force a new call number. Signifies call number
* has been calltoken validated */
NEW_ALLOW_CALLTOKEN_VALIDATED = 3,
};
static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short dcallno, const struct chan_iax2_pvt *cur, int check_dcallno)
{
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 == 0 || cur->peercallno == callno) &&
(check_dcallno ? dcallno == cur->callno : 1) ) {
/* 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) || (cur->transferring == TRANSFER_MEDIAPASS && cur->transfercallno == 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 < ARRAY_LEN(iaxs) - 1; x++) {
if (iaxs[x]) {
max = x + 1;
}
}
maxtrunkcall = max;
if (iaxdebug)
ast_debug(1, "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 (iaxdebug)
ast_debug(1, "New max nontrunk callno is %d\n", max);
}
static int make_trunk(unsigned short callno, int locked)
{
int x;
int res= 0;
struct callno_entry *callno_entry;
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;
}
if (!(callno_entry = get_unused_callno(1, iaxs[callno]->callno_entry->validated))) {
ast_log(LOG_WARNING, "Unable to trunk call: Insufficient space\n");
return -1;
}
x = callno_entry->callno;
ast_mutex_lock(&iaxsl[x]);
/*!
* \note We delete these before switching the slot, because if
* they fire in the meantime, they will generate a warning.
*/
AST_SCHED_DEL(sched, iaxs[callno]->pingid);
AST_SCHED_DEL(sched, iaxs[callno]->lagid);
iaxs[callno]->lagid = iaxs[callno]->pingid = -1;
iaxs[x] = iaxs[callno];
iaxs[x]->callno = x;
/* since we copied over the pvt from a different callno, make sure the old entry is replaced
* before assigning the new one */
if (iaxs[x]->callno_entry) {
iax2_sched_add(sched, MIN_REUSE_TIME * 1000, replace_callno, iaxs[x]->callno_entry);
}
iaxs[x]->callno_entry = callno_entry;
iaxs[callno] = NULL;
/* Update the two timers that should have been started */
iaxs[x]->pingid = iax2_sched_add(sched,
ping_time * 1000, send_ping, (void *)(long)x);
iaxs[x]->lagid = iax2_sched_add(sched,
lagrq_time * 1000, send_lagrq, (void *)(long)x);
if (locked)
ast_mutex_unlock(&iaxsl[callno]);
res = x;
if (!locked)
ast_mutex_unlock(&iaxsl[x]);
ast_debug(1, "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 void store_by_transfercallno(struct chan_iax2_pvt *pvt)
{
if (!pvt->transfercallno) {
ast_log(LOG_ERROR, "This should not be called without a transfer call number.\n");
return;
}
ao2_link(iax_transfercallno_pvts, pvt);
}
static void remove_by_transfercallno(struct chan_iax2_pvt *pvt)
{
if (!pvt->transfercallno) {
ast_log(LOG_ERROR, "This should not be called without a transfer call number.\n");
return;
}
ao2_unlink(iax_transfercallno_pvts, pvt);
}
static void store_by_peercallno(struct chan_iax2_pvt *pvt)
{
if (!pvt->peercallno) {
ast_log(LOG_ERROR, "This should not be called without a peer call number.\n");
return;
}
ao2_link(iax_peercallno_pvts, pvt);
}
static void remove_by_peercallno(struct chan_iax2_pvt *pvt)
{
if (!pvt->peercallno) {
ast_log(LOG_ERROR, "This should not be called without a peer call number.\n");
return;
}
ao2_unlink(iax_peercallno_pvts, pvt);
}
static int addr_range_delme_cb(void *obj, void *arg, int flags)
{
struct addr_range *lim = obj;
lim->delme = 1;
return 0;
}
static int addr_range_hash_cb(const void *obj, const int flags)
{
const struct addr_range *lim = obj;
struct sockaddr_in sin;
ast_sockaddr_to_sin(&lim->ha.addr, &sin);
return abs((int) sin.sin_addr.s_addr);
}
static int addr_range_cmp_cb(void *obj, void *arg, int flags)
{
struct addr_range *lim1 = obj, *lim2 = arg;
return (!(ast_sockaddr_cmp_addr(&lim1->ha.addr, &lim2->ha.addr)) &&
!(ast_sockaddr_cmp_addr(&lim1->ha.netmask, &lim2->ha.netmask))) ?
CMP_MATCH | CMP_STOP : 0;
}
static int peercnt_hash_cb(const void *obj, const int flags)
{
const struct peercnt *peercnt = obj;
return abs((int) peercnt->addr);
}
static int peercnt_cmp_cb(void *obj, void *arg, int flags)
{
struct peercnt *peercnt1 = obj, *peercnt2 = arg;
return (peercnt1->addr == peercnt2->addr) ? CMP_MATCH | CMP_STOP : 0;
}
static int addr_range_match_address_cb(void *obj, void *arg, int flags)
{
struct addr_range *addr_range = obj;
struct sockaddr_in *sin = arg;
struct sockaddr_in ha_netmask_sin;
struct sockaddr_in ha_addr_sin;
ast_sockaddr_to_sin(&addr_range->ha.netmask, &ha_netmask_sin);
ast_sockaddr_to_sin(&addr_range->ha.addr, &ha_addr_sin);
if ((sin->sin_addr.s_addr & ha_netmask_sin.sin_addr.s_addr) == ha_addr_sin.sin_addr.s_addr) {
return CMP_MATCH | CMP_STOP;
}
return 0;
}
/*!
* \internal
*
* \brief compares sin to calltoken_ignores table to determine if validation is required.
*/
static int calltoken_required(struct sockaddr_in *sin, const char *name, int subclass)
{
struct addr_range *addr_range;
struct iax2_peer *peer = NULL;
struct iax2_user *user = NULL;
/* if no username is given, check for guest accounts */
const char *find = S_OR(name, "guest");
int res = 1; /* required by default */
int optional = 0;
enum calltoken_peer_enum calltoken_required = CALLTOKEN_DEFAULT;
/* There are only two cases in which calltoken validation is not required.
* Case 1. sin falls within the list of address ranges specified in the calltoken optional table and
* the peer definition has not set the requirecalltoken option.
* Case 2. Username is a valid peer/user, and that peer has requirecalltoken set either auto or no.
*/
/* ----- Case 1 ----- */
if ((addr_range = ao2_callback(calltoken_ignores, 0, addr_range_match_address_cb, sin))) {
ao2_ref(addr_range, -1);
optional = 1;
}
/* ----- Case 2 ----- */
if ((subclass == IAX_COMMAND_NEW) && (user = find_user(find))) {
calltoken_required = user->calltoken_required;
} else if ((subclass == IAX_COMMAND_NEW) && (user = realtime_user(find, sin))) {
calltoken_required = user->calltoken_required;
} else if ((subclass != IAX_COMMAND_NEW) && (peer = find_peer(find, 0))) {
calltoken_required = peer->calltoken_required;
} else if ((subclass != IAX_COMMAND_NEW) && (peer = realtime_peer(find, sin))) {
calltoken_required = peer->calltoken_required;
}
if (peer) {
peer_unref(peer);
}
if (user) {
user_unref(user);
}
ast_debug(1, "Determining if address %s with username %s requires calltoken validation. Optional = %d calltoken_required = %d \n", ast_inet_ntoa(sin->sin_addr), name, optional, calltoken_required);
if (((calltoken_required == CALLTOKEN_NO) || (calltoken_required == CALLTOKEN_AUTO)) ||
(optional && (calltoken_required == CALLTOKEN_DEFAULT))) {
res = 0;
}
return res;
}
/*!
* \internal
*
* \brief set peercnt callno limit.
*
* \details
* First looks in custom definitions. If not found, global limit
* is used. Entries marked as reg already have
* a custom limit set by a registration and are not modified.
*/
static void set_peercnt_limit(struct peercnt *peercnt)
{
uint16_t limit = global_maxcallno;
struct addr_range *addr_range;
struct sockaddr_in sin = {
.sin_addr.s_addr = peercnt->addr,
};
if (peercnt->reg && peercnt->limit) {
return; /* this peercnt has a custom limit set by a registration */
}
if ((addr_range = ao2_callback(callno_limits, 0, addr_range_match_address_cb, &sin))) {
limit = addr_range->limit;
ast_debug(1, "custom addr_range %d found for %s\n", limit, ast_inet_ntoa(sin.sin_addr));
ao2_ref(addr_range, -1);
}
peercnt->limit = limit;
}
/*!
* \internal
* \brief sets limits for all peercnts in table. done on reload to reflect changes in conf.
*/
static int set_peercnt_limit_all_cb(void *obj, void *arg, int flags)
{
struct peercnt *peercnt = obj;
set_peercnt_limit(peercnt);
ast_debug(1, "Reset limits for peercnts table\n");
return 0;
}
/*!
* \internal
* \brief returns match if delme is set.
*/
static int prune_addr_range_cb(void *obj, void *arg, int flags)
{
struct addr_range *addr_range = obj;
return addr_range->delme ? CMP_MATCH : 0;
}
/*!
* \internal
* \brief modifies peercnt entry in peercnts table. Used to set custom limit or mark a registered ip
*/
static void peercnt_modify(unsigned char reg, uint16_t limit, struct ast_sockaddr *sockaddr)
{
/* this function turns off and on custom callno limits set by peer registration */
struct peercnt *peercnt;
struct peercnt tmp = {
.addr = 0,
};
struct sockaddr_in sin;
ast_sockaddr_to_sin(sockaddr, &sin);
tmp.addr = sin.sin_addr.s_addr;
if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
peercnt->reg = reg;
if (limit) {
peercnt->limit = limit;
} else {
set_peercnt_limit(peercnt);
}
ast_debug(1, "peercnt entry %s modified limit:%d registered:%d", ast_inet_ntoa(sin.sin_addr), peercnt->limit, peercnt->reg);
ao2_ref(peercnt, -1); /* decrement ref from find */
}
}
/*!
* \internal
* \brief adds an ip to the peercnts table, increments connection count if it already exists
*
* \details First searches for the address in the peercnts table. If found
* the current count is incremented. If not found a new peercnt is allocated
* and linked into the peercnts table with a call number count of 1.
*/
static int peercnt_add(struct sockaddr_in *sin)
{
struct peercnt *peercnt;
unsigned long addr = sin->sin_addr.s_addr;
int res = 0;
struct peercnt tmp = {
.addr = addr,
};
/* Reasoning for peercnts container lock: Two identical ip addresses
* could be added by different threads at the "same time". Without the container
* lock, both threads could alloc space for the same object and attempt
* to link to table. With the lock, one would create the object and link
* to table while the other would find the already created peercnt object
* rather than creating a new one. */
ao2_lock(peercnts);
if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
ao2_lock(peercnt);
} else if ((peercnt = ao2_alloc(sizeof(*peercnt), NULL))) {
ao2_lock(peercnt);
/* create and set defaults */
peercnt->addr = addr;
set_peercnt_limit(peercnt);
/* guarantees it does not go away after unlocking table
* ao2_find automatically adds this */
ao2_link(peercnts, peercnt);
} else {
ao2_unlock(peercnts);
return -1;
}
/* check to see if the address has hit its callno limit. If not increment cur. */
if (peercnt->limit > peercnt->cur) {
peercnt->cur++;
ast_debug(1, "ip callno count incremented to %d for %s\n", peercnt->cur, ast_inet_ntoa(sin->sin_addr));
} else { /* max num call numbers for this peer has been reached! */
ast_log(LOG_ERROR, "maxcallnumber limit of %d for %s has been reached!\n", peercnt->limit, ast_inet_ntoa(sin->sin_addr));
res = -1;
}
/* clean up locks and ref count */
ao2_unlock(peercnt);
ao2_unlock(peercnts);
ao2_ref(peercnt, -1); /* decrement ref from find/alloc, only the container ref remains. */
return res;
}
/*!
* \internal
* \brief decrements a peercnts table entry
*/
static void peercnt_remove(struct peercnt *peercnt)
{
struct sockaddr_in sin = {
.sin_addr.s_addr = peercnt->addr,
};
if (peercnt) {
/* Container locked here since peercnt may be unlinked from list. If left unlocked,
* peercnt_add could try and grab this entry from the table and modify it at the
* "same time" this thread attemps to unlink it.*/
ao2_lock(peercnts);
peercnt->cur--;
ast_debug(1, "ip callno count decremented to %d for %s\n", peercnt->cur, ast_inet_ntoa(sin.sin_addr));
/* if this was the last connection from the peer remove it from table */
if (peercnt->cur == 0) {
ao2_unlink(peercnts, peercnt);/* decrements ref from table, last ref is left to scheduler */
}
ao2_unlock(peercnts);
}
}
/*!
* \internal
* \brief called by scheduler to decrement object
*/
static int peercnt_remove_cb(const void *obj)
{
struct peercnt *peercnt = (struct peercnt *) obj;
peercnt_remove(peercnt);
ao2_ref(peercnt, -1); /* decrement ref from scheduler */
return 0;
}
/*!
* \internal
* \brief decrements peercnts connection count, finds by addr
*/
static int peercnt_remove_by_addr(struct sockaddr_in *sin)
{
struct peercnt *peercnt;
struct peercnt tmp = {
.addr = sin->sin_addr.s_addr,
};
if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
peercnt_remove(peercnt);
ao2_ref(peercnt, -1); /* decrement ref from find */
}
return 0;
}
/*!
* \internal
* \brief Create callno_limit entry based on configuration
*/
static void build_callno_limits(struct ast_variable *v)
{
struct addr_range *addr_range = NULL;
struct addr_range tmp;
struct ast_ha *ha;
int limit;
int error;
int found;
for (; v; v = v->next) {
limit = -1;
error = 0;
found = 0;
ha = ast_append_ha("permit", v->name, NULL, &error);
/* check for valid config information */
if (error) {
ast_log(LOG_ERROR, "Call number limit for %s could not be added, Invalid address range\n.", v->name);
continue;
} else if ((sscanf(v->value, "%d", &limit) != 1) || (limit < 0)) {
ast_log(LOG_ERROR, "Call number limit for %s could not be added. Invalid limit %s\n.", v->name, v->value);
ast_free_ha(ha);
continue;
}
ast_copy_ha(ha, &tmp.ha);
/* find or create the addr_range */
if ((addr_range = ao2_find(callno_limits, &tmp, OBJ_POINTER))) {
ao2_lock(addr_range);
found = 1;
} else if (!(addr_range = ao2_alloc(sizeof(*addr_range), NULL))) {
ast_free_ha(ha);
return; /* out of memory */
}
/* copy over config data into addr_range object */
ast_copy_ha(ha, &addr_range->ha); /* this is safe because only one ha is possible for each limit */
ast_free_ha(ha); /* cleanup the tmp ha */
addr_range->limit = limit;
addr_range->delme = 0;
/* cleanup */
if (found) {
ao2_unlock(addr_range);
} else {
ao2_link(callno_limits, addr_range);
}
ao2_ref(addr_range, -1); /* decrement ref from ao2_find and ao2_alloc, only container ref remains */
}
}
/*!
* \internal
* \brief Create calltoken_ignores entry based on configuration
*/
static int add_calltoken_ignore(const char *addr)
{
struct addr_range tmp;
struct addr_range *addr_range = NULL;
struct ast_ha *ha = NULL;
int error = 0;
if (ast_strlen_zero(addr)) {
ast_log(LOG_WARNING, "invalid calltokenoptional %s\n", addr);
return -1;
}
ha = ast_append_ha("permit", addr, NULL, &error);
/* check for valid config information */
if (error) {
ast_log(LOG_WARNING, "Error %d creating calltokenoptional entry %s\n", error, addr);
return -1;
}
ast_copy_ha(ha, &tmp.ha);
/* find or create the addr_range */
if ((addr_range = ao2_find(calltoken_ignores, &tmp, OBJ_POINTER))) {
ao2_lock(addr_range);
addr_range->delme = 0;
ao2_unlock(addr_range);
} else if ((addr_range = ao2_alloc(sizeof(*addr_range), NULL))) {
/* copy over config data into addr_range object */
ast_copy_ha(ha, &addr_range->ha); /* this is safe because only one ha is possible */
ao2_link(calltoken_ignores, addr_range);
} else {
ast_free_ha(ha);
return -1;
}
ast_free_ha(ha);
ao2_ref(addr_range, -1); /* decrement ref from ao2_find and ao2_alloc, only container ref remains */
return 0;
}
static char *handle_cli_iax2_show_callno_limits(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ao2_iterator i;
struct peercnt *peercnt;
struct sockaddr_in sin;
int found = 0;
switch (cmd) {
case CLI_INIT:
e->command = "iax2 show callnumber usage";
e->usage =
"Usage: iax2 show callnumber usage [IP address]\n"
" Shows current IP addresses which are consuming iax2 call numbers\n";
return NULL;
case CLI_GENERATE:
return NULL;
case CLI_HANDLER:
if (a->argc < 4 || a->argc > 5)
return CLI_SHOWUSAGE;
ast_cli(a->fd, "%-15s %-12s %-12s\n", "Address", "Callno Usage", "Callno Limit");
i = ao2_iterator_init(peercnts, 0);
while ((peercnt = ao2_iterator_next(&i))) {
sin.sin_addr.s_addr = peercnt->addr;
if (a->argc == 5 && (!strcasecmp(a->argv[4], ast_inet_ntoa(sin.sin_addr)))) {
ast_cli(a->fd, "%-15s %-12d %-12d\n", ast_inet_ntoa(sin.sin_addr), peercnt->cur, peercnt->limit);
found = 1;
break;
} else {
ast_cli(a->fd, "%-15s %-12d %-12d\n", ast_inet_ntoa(sin.sin_addr), peercnt->cur, peercnt->limit);
}
ao2_ref(peercnt, -1);
}
ao2_iterator_destroy(&i);
if (a->argc == 4) {
ast_cli(a->fd, "\nNon-CallToken Validation Callno Limit: %d\n"
"Non-CallToken Validated Callno Used: %d\n",
global_maxcallno_nonval,
total_nonval_callno_used);
ast_cli(a->fd, "Total Available Callno: %d\n"
"Regular Callno Available: %d\n"
"Trunk Callno Available: %d\n",
ao2_container_count(callno_pool) + ao2_container_count(callno_pool_trunk),
ao2_container_count(callno_pool),
ao2_container_count(callno_pool_trunk));
} else if (a->argc == 5 && !found) {
ast_cli(a->fd, "No callnumber table entries for %s found\n", a->argv[4] );
}
return CLI_SUCCESS;
default:
return NULL;
}
}
static struct callno_entry *get_unused_callno(int trunk, int validated)
{
struct callno_entry *callno_entry = NULL;
if ((!ao2_container_count(callno_pool) && !trunk) || (!ao2_container_count(callno_pool_trunk) && trunk)) {
ast_log(LOG_WARNING, "Out of CallNumbers\n");
/* Minor optimization for the extreme case. */
return NULL;
}
/* the callno_pool container is locked here primarily to ensure thread
* safety of the total_nonval_callno_used check and increment */
ao2_lock(callno_pool);
/* only a certain number of nonvalidated call numbers should be allocated.
* If there ever is an attack, this separates the calltoken validating
* users from the non calltoken validating users. */
if (!validated && (total_nonval_callno_used >= global_maxcallno_nonval)) {
ast_log(LOG_WARNING, "NON-CallToken callnumber limit is reached. Current:%d Max:%d\n", total_nonval_callno_used, global_maxcallno_nonval);
ao2_unlock(callno_pool);
return NULL;
}
/* unlink the object from the container, taking over ownership
* of the reference the container had to the object */
callno_entry = ao2_find((trunk ? callno_pool_trunk : callno_pool), NULL, OBJ_POINTER | OBJ_UNLINK | OBJ_CONTINUE);
if (callno_entry) {
callno_entry->validated = validated;
if (!validated) {
total_nonval_callno_used++;
}
}
ao2_unlock(callno_pool);
return callno_entry;
}
static int replace_callno(const void *obj)
{
struct callno_entry *callno_entry = (struct callno_entry *) obj;
/* the callno_pool container is locked here primarily to ensure thread
* safety of the total_nonval_callno_used check and decrement */
ao2_lock(callno_pool);
if (!callno_entry->validated && (total_nonval_callno_used != 0)) {
total_nonval_callno_used--;
} else if (!callno_entry->validated && (total_nonval_callno_used == 0)) {
ast_log(LOG_ERROR, "Attempted to decrement total non calltoken validated callnumbers below zero... Callno is:%d \n", callno_entry->callno);
}
if (callno_entry->callno < TRUNK_CALL_START) {
ao2_link(callno_pool, callno_entry);
} else {
ao2_link(callno_pool_trunk, callno_entry);
}
ao2_ref(callno_entry, -1); /* only container ref remains */
ao2_unlock(callno_pool);
return 0;
}
static int callno_hash(const void *obj, const int flags)
{
return abs(ast_random());
}
static int create_callno_pools(void)
{
uint16_t i;
if (!(callno_pool = ao2_container_alloc(CALLNO_POOL_BUCKETS, callno_hash, NULL))) {
return -1;
}
if (!(callno_pool_trunk = ao2_container_alloc(CALLNO_POOL_BUCKETS, callno_hash, NULL))) {
return -1;
}
/* start at 2, 0 and 1 are reserved */
for (i = 2; i <= IAX_MAX_CALLS; i++) {
struct callno_entry *callno_entry;
if (!(callno_entry = ao2_alloc(sizeof(*callno_entry), NULL))) {
return -1;
}
callno_entry->callno = i;
if (i < TRUNK_CALL_START) {
ao2_link(callno_pool, callno_entry);
} else {
ao2_link(callno_pool_trunk, callno_entry);
}
ao2_ref(callno_entry, -1);
}
return 0;
}
/*!
* \internal
* \brief Schedules delayed removal of iax2_pvt call number data
*
* \note After MIN_REUSE_TIME has passed for a destroyed iax2_pvt, the callno is
* avaliable again, and the address from the previous connection must be decremented
* from the peercnts table. This function schedules these operations to take place.
*/
static void sched_delay_remove(struct sockaddr_in *sin, struct callno_entry *callno_entry)
{
int i;
struct peercnt *peercnt;
struct peercnt tmp = {
.addr = sin->sin_addr.s_addr,
};
if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
/* refcount is incremented with ao2_find. keep that ref for the scheduler */
ast_debug(1, "schedule decrement of callno used for %s in %d seconds\n", ast_inet_ntoa(sin->sin_addr), MIN_REUSE_TIME);
i = iax2_sched_add(sched, MIN_REUSE_TIME * 1000, peercnt_remove_cb, peercnt);
if (i == -1) {
ao2_ref(peercnt, -1);
}
}
iax2_sched_add(sched, MIN_REUSE_TIME * 1000, replace_callno, callno_entry);
}
/*!
* \internal
* \brief returns whether or not a frame is capable of starting a new IAX2 dialog.
*
* \note For this implementation, inbound pokes should _NOT_ be capable of allocating
* a new callno.
*/
static inline int attribute_pure iax2_allow_new(int frametype, int subclass, int inbound)
{
if (frametype != AST_FRAME_IAX) {
return 0;
}
switch (subclass) {
case IAX_COMMAND_NEW:
case IAX_COMMAND_REGREQ:
case IAX_COMMAND_FWDOWNL:
case IAX_COMMAND_REGREL:
return 1;
case IAX_COMMAND_POKE:
if (!inbound) {
return 1;
}
break;
}
return 0;
}
/*
* \note Calling this function while holding another pvt lock can cause a deadlock.
*/
static int __find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd, int return_locked, int check_dcallno)
{
int res = 0;
int x;
/* this call is calltoken validated as long as it is either NEW_FORCE
* or NEW_ALLOW_CALLTOKEN_VALIDATED */
int validated = (new > NEW_ALLOW) ? 1 : 0;
char host[80];
if (new <= NEW_ALLOW) {
if (callno) {
struct chan_iax2_pvt *pvt;
struct chan_iax2_pvt tmp_pvt = {
.callno = dcallno,
.peercallno = callno,
.transfercallno = callno,
/* hack!! */
.frames_received = check_dcallno,
};
memcpy(&tmp_pvt.addr, sin, sizeof(tmp_pvt.addr));
/* this works for finding normal call numbers not involving transfering */
if ((pvt = ao2_find(iax_peercallno_pvts, &tmp_pvt, OBJ_POINTER))) {
if (return_locked) {
ast_mutex_lock(&iaxsl[pvt->callno]);
}
res = pvt->callno;
ao2_ref(pvt, -1);
pvt = NULL;
return res;
}
/* this searches for transfer call numbers that might not get caught otherwise */
memset(&tmp_pvt.addr, 0, sizeof(tmp_pvt.addr));
memcpy(&tmp_pvt.transfer, sin, sizeof(tmp_pvt.transfer));
if ((pvt = ao2_find(iax_transfercallno_pvts, &tmp_pvt, OBJ_POINTER))) {
if (return_locked) {
ast_mutex_lock(&iaxsl[pvt->callno]);
}
res = pvt->callno;
ao2_ref(pvt, -1);
pvt = NULL;
return res;
}
}
/* This will occur on the first response to a message that we initiated,
* such as a PING. */
if (dcallno) {
ast_mutex_lock(&iaxsl[dcallno]);
}
if (callno && dcallno && iaxs[dcallno] && !iaxs[dcallno]->peercallno && match(sin, callno, dcallno, iaxs[dcallno], check_dcallno)) {
iaxs[dcallno]->peercallno = callno;
res = dcallno;
store_by_peercallno(iaxs[dcallno]);
if (!res || !return_locked) {
ast_mutex_unlock(&iaxsl[dcallno]);
}
return res;
}
if (dcallno) {
ast_mutex_unlock(&iaxsl[dcallno]);
}
#ifdef IAX_OLD_FIND
/* If we get here, we SHOULD NOT find a call structure for this
callno; if we do, it means that there is a call structure that
has a peer callno but did NOT get entered into the hash table,
which is bad.
If we find a call structure using this old, slow method, output a log
message so we'll know about it. After a few months of leaving this in
place, if we don't hear about people seeing these messages, we can
remove this code for good.
*/
for (x = 1; !res && x < maxnontrunkcall; x++) {
ast_mutex_lock(&iaxsl[x]);
if (iaxs[x]) {
/* Look for an exact match */
if (match(sin, callno, dcallno, iaxs[x], check_dcallno)) {
res = x;
}
}
if (!res || !return_locked)
ast_mutex_unlock(&iaxsl[x]);
}
for (x = TRUNK_CALL_START; !res && x < maxtrunkcall; x++) {
ast_mutex_lock(&iaxsl[x]);
if (iaxs[x]) {
/* Look for an exact match */
if (match(sin, callno, dcallno, iaxs[x], check_dcallno)) {
res = x;
}
}
if (!res || !return_locked)
ast_mutex_unlock(&iaxsl[x]);
}
#endif
}
if (!res && (new >= NEW_ALLOW)) {
struct callno_entry *callno_entry;
/* It may seem odd that we look through the peer list for a name for
* this *incoming* call. Well, it is weird. However, users don't
* have an IP address/port number that we can match against. So,
* this is just checking for a peer that has that IP/port and
* assuming that we have a user of the same name. This isn't always
* correct, but it will be changed if needed after authentication. */
if (!iax2_getpeername(*sin, host, sizeof(host)))
snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
if (peercnt_add(sin)) {
/* This address has hit its callnumber limit. When the limit
* is reached, the connection is not added to the peercnts table.*/
return 0;
}
if (!(callno_entry = get_unused_callno(0, validated))) {
/* since we ran out of space, remove the peercnt
* entry we added earlier */
peercnt_remove_by_addr(sin);
ast_log(LOG_WARNING, "No more space\n");
return 0;
}
x = callno_entry->callno;
ast_mutex_lock(&iaxsl[x]);
iaxs[x] = new_iax(sin, host);
update_max_nontrunk();
if (iaxs[x]) {
if (iaxdebug)
ast_debug(1, "Creating new call structure %d\n", x);
iaxs[x]->callno_entry = callno_entry;
iaxs[x]->sockfd = sockfd;
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]->expiry = min_reg_expire;
iaxs[x]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x);
iaxs[x]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x);
iaxs[x]->amaflags = amaflags;
ast_copy_flags64(iaxs[x], &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_SENDCONNECTEDLINE | IAX_RECVCONNECTEDLINE | IAX_FORCE_ENCRYPT);
ast_string_field_set(iaxs[x], accountcode, accountcode);
ast_string_field_set(iaxs[x], mohinterpret, mohinterpret);
ast_string_field_set(iaxs[x], mohsuggest, mohsuggest);
ast_string_field_set(iaxs[x], parkinglot, default_parkinglot);
if (iaxs[x]->peercallno) {
store_by_peercallno(iaxs[x]);
}
} else {
ast_log(LOG_WARNING, "Out of resources\n");
ast_mutex_unlock(&iaxsl[x]);
replace_callno(callno_entry);
return 0;
}
if (!return_locked)
ast_mutex_unlock(&iaxsl[x]);
res = x;
}
return res;
}
static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd, int full_frame) {
return __find_callno(callno, dcallno, sin, new, sockfd, 0, full_frame);
}
static int find_callno_locked(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd, int full_frame) {
return __find_callno(callno, dcallno, sin, new, sockfd, 1, full_frame);
}
/*!
* \brief Queue a frame to a call's owning asterisk channel
*
* \pre This function assumes that iaxsl[callno] is locked when called.
*
* \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno]
* was valid before calling it, it may no longer be valid after calling it.
* This function may unlock and lock the mutex associated with this callno,
* meaning that another thread may grab it and destroy the call.
*/
static int iax2_queue_frame(int callno, struct ast_frame *f)
{
iax2_lock_owner(callno);
if (iaxs[callno] && iaxs[callno]->owner) {
ast_queue_frame(iaxs[callno]->owner, f);
ast_channel_unlock(iaxs[callno]->owner);
}
return 0;
}
/*!
* \brief Queue a hangup frame on the ast_channel owner
*
* This function queues a hangup frame on the owner of the IAX2 pvt struct that
* is active for the given call number.
*
* \pre Assumes lock for callno is already held.
*
* \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno]
* was valid before calling it, it may no longer be valid after calling it.
* This function may unlock and lock the mutex associated with this callno,
* meaning that another thread may grab it and destroy the call.
*/
static int iax2_queue_hangup(int callno)
{
iax2_lock_owner(callno);
if (iaxs[callno] && iaxs[callno]->owner) {
ast_queue_hangup(iaxs[callno]->owner);
ast_channel_unlock(iaxs[callno]->owner);
}
return 0;
}
/*!
* \brief Queue a control frame on the ast_channel owner
*
* This function queues a control frame on the owner of the IAX2 pvt struct that
* is active for the given call number.
*
* \pre Assumes lock for callno is already held.
*
* \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno]
* was valid before calling it, it may no longer be valid after calling it.
* This function may unlock and lock the mutex associated with this callno,
* meaning that another thread may grab it and destroy the call.
*/
static int iax2_queue_control_data(int callno,
enum ast_control_frame_type control, const void *data, size_t datalen)
{
iax2_lock_owner(callno);
if (iaxs[callno] && iaxs[callno]->owner) {
ast_queue_control_data(iaxs[callno]->owner, control, data, datalen);
ast_channel_unlock(iaxs[callno]->owner);
}
return 0;
}
static void destroy_firmware(struct iax_firmware *cur)
{
/* Close firmware */
if (cur->fwh) {
munmap((void*)cur->fwh, ntohl(cur->fwh->datalen) + sizeof(*(cur->fwh)));
}
close(cur->fd);
ast_free(cur);
}
static int try_firmware(char *s)
{
struct stat stbuf;
struct iax_firmware *cur = NULL;
int ifd, fd, res, len, chunk;
struct ast_iax2_firmware_header *fwh, fwh2;
struct MD5Context md5;
unsigned char sum[16], buf[1024];
char *s2, *last;
if (!(s2 = alloca(strlen(s) + 100))) {
ast_log(LOG_WARNING, "Alloca failed!\n");
return -1;
}
last = strrchr(s, '/');
if (last)
last++;
else
last = s;
snprintf(s2, strlen(s) + 100, "/var/tmp/%s-%ld", last, (unsigned long)ast_random());
if ((res = stat(s, &stbuf) < 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;
ifd = open(s, O_RDONLY);
if (ifd < 0) {
ast_log(LOG_WARNING, "Cannot open '%s': %s\n", s, strerror(errno));
return -1;
}
fd = open(s2, O_RDWR | O_CREAT | O_EXCL, AST_FILE_MODE);
if (fd < 0) {
ast_log(LOG_WARNING, "Cannot open '%s' for writing: %s\n", s2, strerror(errno));
close(ifd);
return -1;
}
/* Unlink our newly created file */
unlink(s2);
/* Now copy the firmware into it */
len = stbuf.st_size;
while(len) {
chunk = len;
if (chunk > sizeof(buf))
chunk = sizeof(buf);
res = read(ifd, buf, chunk);
if (res != chunk) {
ast_log(LOG_WARNING, "Only read %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno));
close(ifd);
close(fd);
return -1;
}
res = write(fd, buf, chunk);
if (res != chunk) {
ast_log(LOG_WARNING, "Only write %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno));
close(ifd);
close(fd);
return -1;
}
len -= chunk;
}
close(ifd);
/* Return to the beginning */
lseek(fd, 0, SEEK_SET);
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((char *)fwh2.devname)) {
ast_log(LOG_WARNING, "No or invalid device type specified for '%s'\n", s);
close(fd);
return -1;
}
fwh = (struct ast_iax2_firmware_header*)mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (fwh == MAP_FAILED) {
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((void*)fwh, stbuf.st_size);
close(fd);
return -1;
}
AST_LIST_TRAVERSE(&firmwares, cur, list) {
if (!strcmp((char *)cur->fwh->devname, (char *)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((void*)fwh, stbuf.st_size);
close(fd);
return 0;
}
}
if (!cur && ((cur = ast_calloc(1, sizeof(*cur))))) {
cur->fd = -1;
AST_LIST_INSERT_TAIL(&firmwares, cur, list);
}
if (cur) {
if (cur->fwh)
munmap((void*)cur->fwh, cur->mmaplen);
if (cur->fd > -1)
close(cur->fd);
cur->fwh = fwh;
cur->fd = fd;
cur->mmaplen = stbuf.st_size;
cur->dead = 0;
}
return 0;
}
static int iax_check_version(char *dev)
{
int res = 0;
struct iax_firmware *cur = NULL;
if (ast_strlen_zero(dev))
return 0;
AST_LIST_LOCK(&firmwares);
AST_LIST_TRAVERSE(&firmwares, cur, list) {
if (!strcmp(dev, (char *)cur->fwh->devname)) {
res = ntohs(cur->fwh->version);
break;
}
}
AST_LIST_UNLOCK(&firmwares);
return res;
}
static int iax_firmware_append(struct iax_ie_data *ied, const unsigned char *dev, unsigned int desc)
{
int res = -1;
unsigned int bs = desc & 0xff;
unsigned int start = (desc >> 8) & 0xffffff;
unsigned int bytes;
struct iax_firmware *cur;
if (ast_strlen_zero((char *)dev) || !bs)
return -1;
start *= bs;
AST_LIST_LOCK(&firmwares);
AST_LIST_TRAVERSE(&firmwares, cur, list) {
if (strcmp((char *)dev, (char *)cur->fwh->devname))
continue;
iax_ie_append_int(ied, IAX_IE_FWBLOCKDESC, desc);
if (start < ntohl(cur->fwh->datalen)) {
bytes = ntohl(cur->fwh->datalen) - start;
if (bytes > bs)
bytes = bs;
iax_ie_append_raw(ied, IAX_IE_FWBLOCKDATA, cur->fwh->data + start, bytes);
} else {
bytes = 0;
iax_ie_append(ied, IAX_IE_FWBLOCKDATA);
}
if (bytes == bs)
res = 0;
else
res = 1;
break;
}
AST_LIST_UNLOCK(&firmwares);
return res;
}
static void reload_firmware(int unload)
{
struct iax_firmware *cur = NULL;
DIR *fwd;
struct dirent *de;
char dir[256], fn[256];
AST_LIST_LOCK(&firmwares);
/* Mark all as dead */
AST_LIST_TRAVERSE(&firmwares, cur, list)
cur->dead = 1;
/* Now that we have marked them dead... load new ones */
if (!unload) {
snprintf(dir, sizeof(dir), "%s/firmware/iax", ast_config_AST_DATA_DIR);
fwd = opendir(dir);
if (fwd) {
while((de = readdir(fwd))) {
if (de->d_name[0] != '.') {
snprintf(fn, sizeof(fn), "%s/%s", dir, de->d_name);
if (!try_firmware(fn)) {
ast_verb(2, "Loaded firmware '%s'\n", de->d_name);
}
}
}
closedir(fwd);
} else
ast_log(LOG_WARNING, "Error opening firmware directory '%s': %s\n", dir, strerror(errno));
}
/* Clean up leftovers */
AST_LIST_TRAVERSE_SAFE_BEGIN(&firmwares, cur, list) {
if (!cur->dead)
continue;
AST_LIST_REMOVE_CURRENT(list);
destroy_firmware(cur);
}
AST_LIST_TRAVERSE_SAFE_END;
AST_LIST_UNLOCK(&firmwares);
}
/*!
* \note This function assumes that iaxsl[callno] is locked when called.
*
* \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno]
* was valid before calling it, it may no longer be valid after calling it.
* This function calls iax2_queue_frame(), which may unlock and lock the mutex
* associated with this callno, meaning that another thread may grab it and destroy the call.
*/
static int __do_deliver(void *data)
{
/* Just deliver the packet by using queueing. This is called by
the IAX thread with the iaxsl lock held. */
struct iax_frame *fr = data;
fr->retrans = -1;
ast_clear_flag(&fr->af, AST_FRFLAG_HAS_TIMING_INFO);
if (iaxs[fr->callno] && !ast_test_flag64(iaxs[fr->callno], IAX_ALREADYGONE))
iax2_queue_frame(fr->callno, &fr->af);
/* Free our iax frame */
iax2_frame_free(fr);
/* And don't run again */
return 0;
}
static int handle_error(void)
{
/* XXX Ideally we should figure out why an error occurred and then abort those
rather than continuing to try. Unfortunately, the published interface does
not seem to work XXX */
#if 0
struct sockaddr_in *sin;
int res;
struct msghdr m;
struct sock_extended_err e;
m.msg_name = NULL;
m.msg_namelen = 0;
m.msg_iov = NULL;
m.msg_control = &e;
m.msg_controllen = sizeof(e);
m.msg_flags = 0;
res = recvmsg(netsocket, &m, MSG_ERRQUEUE);
if (res < 0)
ast_log(LOG_WARNING, "Error detected, but unable to read error: %s\n", strerror(errno));
else {
if (m.msg_controllen) {
sin = (struct sockaddr_in *)SO_EE_OFFENDER(&e);
if (sin)
ast_log(LOG_WARNING, "Receive error from %s\n", ast_inet_ntoa(sin->sin_addr));
else
ast_log(LOG_WARNING, "No address detected??\n");
} else {
ast_log(LOG_WARNING, "Local error: %s\n", strerror(e.ee_errno));
}
}
#endif
return 0;
}
static int transmit_trunk(struct iax_frame *f, struct sockaddr_in *sin, int sockfd)
{
int res;
res = sendto(sockfd, f->data, f->datalen, 0,(struct sockaddr *)sin,
sizeof(*sin));
if (res < 0) {
ast_debug(1, "Received error: %s\n", strerror(errno));
handle_error();
} else
res = 0;
return res;
}
static int send_packet(struct iax_frame *f)
{
int res;
int callno = f->callno;
/* Don't send if there was an error, but return error instead */
if (!callno || !iaxs[callno] || iaxs[callno]->error)
return -1;
/* Called with iaxsl held */
if (iaxdebug)
ast_debug(3, "Sending %d on %d/%d to %s:%d\n", f->ts, callno, iaxs[callno]->peercallno, ast_inet_ntoa(iaxs[callno]->addr.sin_addr), ntohs(iaxs[callno]->addr.sin_port));
if (f->transfer) {
if (iaxdebug)
iax_showframe(f, NULL, 0, &iaxs[callno]->transfer, f->datalen - sizeof(struct ast_iax2_full_hdr));
res = sendto(iaxs[callno]->sockfd, f->data, f->datalen, 0,(struct sockaddr *)&iaxs[callno]->transfer, sizeof(iaxs[callno]->transfer));
} else {
if (iaxdebug)
iax_showframe(f, NULL, 0, &iaxs[callno]->addr, f->datalen - sizeof(struct ast_iax2_full_hdr));
res = sendto(iaxs[callno]->sockfd, f->data, f->datalen, 0,(struct sockaddr *)&iaxs[callno]->addr, sizeof(iaxs[callno]->addr));
}
if (res < 0) {
if (iaxdebug)
ast_debug(1, "Received error: %s\n", strerror(errno));
handle_error();
} else
res = 0;
return res;
}
/*!
* \note Since this function calls iax2_queue_hangup(), the pvt struct
* for the given call number may disappear during its execution.
*/
static int iax2_predestroy(int callno)
{
struct ast_channel *c = NULL;
struct chan_iax2_pvt *pvt = iaxs[callno];
if (!pvt)
return -1;
if (!ast_test_flag64(pvt, IAX_ALREADYGONE)) {
iax2_destroy_helper(pvt);
ast_set_flag64(pvt, IAX_ALREADYGONE);
}
if ((c = pvt->owner)) {
c->tech_pvt = NULL;
iax2_queue_hangup(callno);
pvt->owner = NULL;
ast_module_unref(ast_module_info->self);
}
return 0;
}
static void iax2_destroy(int callno)
{
struct chan_iax2_pvt *pvt = NULL;
struct ast_channel *owner = NULL;
retry:
if ((pvt = iaxs[callno])) {
#if 0
/* iax2_destroy_helper gets called from this function later on. When
* called twice, we get the (previously) familiar FRACK! errors in
* devmode, from the scheduler. An alternative to this approach is to
* reset the scheduler entries to -1 when they're deleted in
* iax2_destroy_helper(). That approach was previously decided to be
* "wrong" because "the memory is going to be deallocated anyway. Why
* should we be resetting those values?" */
iax2_destroy_helper(pvt);
#endif
}
owner = pvt ? pvt->owner : NULL;
if (owner) {
if (ast_channel_trylock(owner)) {
ast_debug(3, "Avoiding IAX destroy deadlock\n");
DEADLOCK_AVOIDANCE(&iaxsl[callno]);
goto retry;
}
}
if (!owner) {
iaxs[callno] = NULL;
}
if (pvt) {
if (!owner) {
pvt->owner = NULL;
} else {
/* If there's an owner, prod it to give up */
/* It is ok to use ast_queue_hangup() here instead of iax2_queue_hangup()
* because we already hold the owner channel lock. */
ast_queue_hangup(owner);
}
if (pvt->peercallno) {
remove_by_peercallno(pvt);
}
if (pvt->transfercallno) {
remove_by_transfercallno(pvt);
}
if (!owner) {
ao2_ref(pvt, -1);
pvt = NULL;
}
}
if (owner) {
ast_channel_unlock(owner);
}
if (callno & 0x4000) {
update_max_trunk();
}
}
static int update_packet(struct iax_frame *f)
{
/* Called with iaxsl lock held, and iaxs[callno] non-NULL */
struct ast_iax2_full_hdr *fh = f->data;
struct ast_frame af;
/* if frame is encrypted. decrypt before updating it. */
if (f->encmethods) {
decode_frame(&f->mydcx, fh, &af, &f->datalen);
}
/* Mark this as a retransmission */
fh->dcallno = ntohs(IAX_FLAG_RETRANS | f->dcallno);
/* Update iseqno */
f->iseqno = iaxs[f->callno]->iseqno;
fh->iseqno = f->iseqno;
/* Now re-encrypt the frame */
if (f->encmethods) {
/* since this is a retransmit frame, create a new random padding
* before re-encrypting. */
build_rand_pad(f->semirand, sizeof(f->semirand));
encrypt_frame(&f->ecx, fh, f->semirand, &f->datalen);
}
return 0;
}
static int attempt_transmit(const void *data);
static void __attempt_transmit(const void *data)
{
/* Attempt to transmit the frame to the remote peer...
Called without iaxsl held. */
struct iax_frame *f = (struct iax_frame *)data;
int freeme = 0;
int callno = f->callno;
/* Make sure this call is still active */
if (callno)
ast_mutex_lock(&iaxsl[callno]);
if (callno && iaxs[callno]) {
if ((f->retries < 0) /* Already ACK'd */ ||
(f->retries >= max_retries) /* Too many attempts */) {
/* Record an error if we've transmitted too many times */
if (f->retries >= max_retries) {
if (f->transfer) {
/* Transfer timeout */
send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_TXREJ, 0, NULL, 0, -1);
} else if (f->final) {
iax2_destroy(callno);
} else {
if (iaxs[callno]->owner)
ast_log(LOG_WARNING, "Max retries exceeded to host %s on %s (type = %d, subclass = %u, ts=%d, seqno=%d)\n", ast_inet_ntoa(iaxs[f->callno]->addr.sin_addr),iaxs[f->callno]->owner->name , f->af.frametype, f->af.subclass.integer, f->ts, f->oseqno);
iaxs[callno]->error = ETIMEDOUT;
if (iaxs[callno]->owner) {
struct ast_frame fr = { AST_FRAME_CONTROL, { AST_CONTROL_HANGUP }, .data.uint32 = AST_CAUSE_DESTINATION_OUT_OF_ORDER };
/* Hangup the fd */
iax2_queue_frame(callno, &fr); /* XXX */
/* Remember, owner could disappear */
if (iaxs[callno] && iaxs[callno]->owner)
iaxs[callno]->owner->hangupcause = AST_CAUSE_DESTINATION_OUT_OF_ORDER;
} else {
if (iaxs[callno]->reg) {
memset(&iaxs[callno]->reg->us, 0, sizeof(iaxs[callno]->reg->us));
iaxs[callno]->reg->regstate = REG_STATE_TIMEOUT;
iaxs[callno]->reg->refresh = IAX_DEFAULT_REG_EXPIRE;
}
iax2_destroy(callno);
}
}
}
freeme = 1;
} else {
/* Update it if it needs it */
update_packet(f);
/* Attempt transmission */
send_packet(f);
f->retries++;
/* Try again later after 10 times as long */
f->retrytime *= 10;
if (f->retrytime > MAX_RETRY_TIME)
f->retrytime = MAX_RETRY_TIME;
/* Transfer messages max out at one second */
if (f->transfer && (f->retrytime > 1000))
f->retrytime = 1000;
f->retrans = iax2_sched_add(sched, f->retrytime, attempt_transmit, f);
}
} else {
/* Make sure it gets freed */
f->retries = -1;
freeme = 1;
}
if (freeme) {
/* Don't attempt delivery, just remove it from the queue */
AST_LIST_REMOVE(&frame_queue[callno], f, list);
ast_mutex_unlock(&iaxsl[callno]);
f->retrans = -1; /* this is safe because this is the scheduled function */
/* Free the IAX frame */
iax2_frame_free(f);
} else if (callno) {
ast_mutex_unlock(&iaxsl[callno]);
}
}
static int attempt_transmit(const void *data)
{
#ifdef SCHED_MULTITHREADED
if (schedule_action(__attempt_transmit, data))
#endif
__attempt_transmit(data);
return 0;
}
static char *handle_cli_iax2_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct iax2_peer *peer = NULL;
struct iax2_user *user = NULL;
static const char * const choices[] = { "all", NULL };
char *cmplt;
switch (cmd) {
case CLI_INIT:
e->command = "iax2 prune realtime";
e->usage =
"Usage: iax2 prune realtime [<peername>|all]\n"
" Prunes object(s) from the cache\n";
return NULL;
case CLI_GENERATE:
if (a->pos == 3) {
cmplt = ast_cli_complete(a->word, choices, a->n);
if (!cmplt)
cmplt = complete_iax2_peers(a->line, a->word, a->pos, a->n - sizeof(choices), IAX_RTCACHEFRIENDS);
return cmplt;
}
return NULL;
}
if (a->argc != 4)
return CLI_SHOWUSAGE;
if (!strcmp(a->argv[3], "all")) {
prune_users();
prune_peers();
ast_cli(a->fd, "Cache flushed successfully.\n");
return CLI_SUCCESS;
}
peer = find_peer(a->argv[3], 0);
user = find_user(a->argv[3]);
if (peer || user) {
if (peer) {
if (ast_test_flag64(peer, IAX_RTCACHEFRIENDS)) {
ast_set_flag64(peer, IAX_RTAUTOCLEAR);
expire_registry(peer_ref(peer));
ast_cli(a->fd, "Peer %s was removed from the cache.\n", a->argv[3]);
} else {
ast_cli(a->fd, "Peer %s is not eligible for this operation.\n", a->argv[3]);
}
peer_unref(peer);
}
if (user) {
if (ast_test_flag64(user, IAX_RTCACHEFRIENDS)) {
ast_set_flag64(user, IAX_RTAUTOCLEAR);
ast_cli(a->fd, "User %s was removed from the cache.\n", a->argv[3]);
} else {
ast_cli(a->fd, "User %s is not eligible for this operation.\n", a->argv[3]);
}
ao2_unlink(users,user);
user_unref(user);
}
} else {
ast_cli(a->fd, "%s was not found in the cache.\n", a->argv[3]);
}
return CLI_SUCCESS;
}
static char *handle_cli_iax2_test_losspct(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
switch (cmd) {
case CLI_INIT:
e->command = "iax2 test losspct";
e->usage =
"Usage: iax2 test losspct <percentage>\n"
" For testing, throws away <percentage> percent of incoming packets\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != 4)
return CLI_SHOWUSAGE;
test_losspct = atoi(a->argv[3]);
return CLI_SUCCESS;
}
#ifdef IAXTESTS
static char