[Pkg-voip-commits] r7592 - in /asterisk/trunk/debian: NEWS changelog patches/AST-2009-006 patches/series
tzafrir-guest at alioth.debian.org
tzafrir-guest at alioth.debian.org
Sat Sep 12 21:30:58 UTC 2009
Author: tzafrir-guest
Date: Sat Sep 12 21:30:57 2009
New Revision: 7592
URL: http://svn.debian.org/wsvn/pkg-voip/?sc=1&rev=7592
Log:
Patch AST-2009-006: Closes: #539473 but breaks IAX2 compatibility.
Added:
asterisk/trunk/debian/patches/AST-2009-006
Modified:
asterisk/trunk/debian/NEWS
asterisk/trunk/debian/changelog
asterisk/trunk/debian/patches/series
Modified: asterisk/trunk/debian/NEWS
URL: http://svn.debian.org/wsvn/pkg-voip/asterisk/trunk/debian/NEWS?rev=7592&op=diff
==============================================================================
--- asterisk/trunk/debian/NEWS (original)
+++ asterisk/trunk/debian/NEWS Sat Sep 12 21:30:57 2009
@@ -1,3 +1,14 @@
+asterisk (1:1.6.2.0~dfsg~rc1-1) unstable; urgency=low
+
+ The fix for AST-2009-006 breaks IAX2 line protocol by adding an extra
+ step in the IAX2 handshaking. To allow working with unmodified IAX2
+ implementations, add the following two lines under [general] in iax.conf:
+
+ calltokenoptional = 0.0.0.0/0.0.0.0
+ maxcallnumbers = 16382
+
+ -- Tzafrir Cohen <tzafrir.cohen at xorcom.com> Sun, 13 Sep 2009 00:25:09 +0300
+
asterisk (1:1.4.13~dfsg-1) unstable; urgency=medium
Since this version, ODBC and IMAP storage for Voicemail are provided.
Modified: asterisk/trunk/debian/changelog
URL: http://svn.debian.org/wsvn/pkg-voip/asterisk/trunk/debian/changelog?rev=7592&op=diff
==============================================================================
--- asterisk/trunk/debian/changelog (original)
+++ asterisk/trunk/debian/changelog Sat Sep 12 21:30:57 2009
@@ -13,8 +13,9 @@
* Patch hardware_dtmf_mute_fix removed: Applied upstream.
* No need for a separate app_directory_odbc (will use app_voicemail_odbc).
* Fix name of voicemail 'openssl' dep. (Thomas Renard) (Closes: #539150)
-
- -- Faidon Liambotis <paravoid at debian.org> Mon, 31 Aug 2009 21:50:05 +0300
+ * Patch AST-2009-006: Closes: #539473 but breaks IAX2 compatibility.
+
+ -- Tzafrir Cohen <tzafrir.cohen at xorcom.com> Sun, 13 Sep 2009 00:23:16 +0300
asterisk (1:1.6.2.0~dfsg~beta3-1) unstable; urgency=low
Added: asterisk/trunk/debian/patches/AST-2009-006
URL: http://svn.debian.org/wsvn/pkg-voip/asterisk/trunk/debian/patches/AST-2009-006?rev=7592&op=file
==============================================================================
--- asterisk/trunk/debian/patches/AST-2009-006 (added)
+++ asterisk/trunk/debian/patches/AST-2009-006 Sat Sep 12 21:30:57 2009
@@ -1,0 +1,1986 @@
+Fix for AST-2009-006: IAX2 Call Number Resource Exhaustion
+
+This is a huge patch that also breaks compatibility with other IAX2
+implementation.
+
+CVE: CVE-2009-2651
+
+Source: http://downloads.asterisk.org/pub/security/AST-2009-006-1.6.2.diff.txt
+See also: http://downloads.asterisk.org/pub/security/AST-2009-006.html
+Specs: http://downloads.asterisk.org/pub/security/IAX2-security.html
+
+Fixed in upstream SVN. Should be included in next release of asterisk-addons.
+
+Index: channels/iax2-parser.h
+===================================================================
+--- a/channels/iax2-parser.h (revision 215955)
++++ b/channels/iax2-parser.h (working copy)
+@@ -77,6 +77,8 @@
+ struct ast_variable *vars;
+ char *osptokenblock[IAX_MAX_OSPBLOCK_NUM];
+ unsigned int ospblocklength[IAX_MAX_OSPBLOCK_NUM];
++ unsigned char calltoken;
++ unsigned char *calltokendata;
+ };
+
+ #define DIRECTION_INGRESS 1
+Index: channels/chan_iax2.c
+===================================================================
+--- a/channels/chan_iax2.c (revision 215955)
++++ b/channels/chan_iax2.c (working copy)
+@@ -385,6 +385,21 @@
+
+ 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);
+@@ -412,6 +427,7 @@
+ 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 {
+@@ -463,10 +479,12 @@
+ 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))
+@@ -615,6 +633,8 @@
+ 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
+@@ -757,8 +777,18 @@
+ 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;
+ };
+
++/*! 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
+ *
+@@ -768,6 +798,10 @@
+ */
+ static AST_LIST_HEAD_STATIC(frame_queue, iax_frame);
+
++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.
+@@ -785,6 +819,57 @@
+ #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 {
+@@ -932,27 +1017,6 @@
+ static ast_mutex_t iaxsl[ARRAY_LEN(iaxs)];
+
+ /*!
+- * \brief The last time a call number was used
+- *
+- * It is important to know the last time that a call number was used locally so
+- * that it is not used again too soon. The reason for this is the same as the
+- * reason that the TCP protocol state machine requires a "TIME WAIT" state.
+- *
+- * For example, say that a call is up. Then, the remote side sends a HANGUP,
+- * which we respond to with an ACK. However, there is no way to know whether
+- * the ACK made it there successfully. If it were to get lost, the remote
+- * side may retransmit the HANGUP. If in the meantime, this call number has
+- * been reused locally, given the right set of circumstances, this retransmitted
+- * HANGUP could potentially improperly hang up the new session. So, to avoid
+- * this potential issue, we must wait a specified timeout period before reusing
+- * a local call number.
+- *
+- * The specified time that we must wait before reusing a local call number is
+- * defined as MIN_REUSE_TIME, with a default of 60 seconds.
+- */
+-static struct timeval lastused[ARRAY_LEN(iaxs)];
+-
+-/*!
+ * * \brief Another container of iax2_pvt structures
+ *
+ * Active IAX2 pvt stucts used during transfering a call are stored here.
+@@ -1075,6 +1139,9 @@
+ 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 const struct ast_channel_tech iax2_tech = {
+ .type = "IAX2",
+@@ -1596,6 +1663,8 @@
+
+ ast_mutex_lock(&iaxsl[pvt->callno]);
+ iax2_destroy_helper(pvt);
++ sched_delay_remove(&pvt->addr, pvt->callno_entry);
++ pvt->callno_entry = NULL;
+ ast_mutex_unlock(&iaxsl[pvt->callno]);
+
+ /* Already gone */
+@@ -1684,11 +1753,20 @@
+ }
+ 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,
++};
+
+-#define NEW_PREVENT 0
+-#define NEW_ALLOW 1
+-#define NEW_FORCE 2
+-
+ static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short dcallno, const struct chan_iax2_pvt *cur, int check_dcallno)
+ {
+ if ((cur->addr.sin_addr.s_addr == sin->sin_addr.s_addr) &&
+@@ -1744,7 +1822,7 @@
+ {
+ int x;
+ int res= 0;
+- struct timeval now = ast_tvnow();
++ 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;
+@@ -1753,36 +1831,44 @@
+ ast_log(LOG_WARNING, "Call %d is already a trunk\n", callno);
+ return -1;
+ }
+- for (x = TRUNK_CALL_START; x < ARRAY_LEN(iaxs) - 1; x++) {
+- ast_mutex_lock(&iaxsl[x]);
+- if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) {
+- /*!
+- * \note We delete these before switching the slot, because if
+- * they fire in the meantime, they will generate a warning.
+- */
+- ast_sched_thread_del(sched, iaxs[callno]->pingid);
+- ast_sched_thread_del(sched, iaxs[callno]->lagid);
+- iaxs[x] = iaxs[callno];
+- iaxs[x]->callno = x;
+- 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]);
+- break;
+- }
+- ast_mutex_unlock(&iaxsl[x]);
+- }
+- if (x >= ARRAY_LEN(iaxs) - 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_thread_del(sched, iaxs[callno]->pingid);
++ ast_sched_thread_del(sched, iaxs[callno]->lagid);
++ 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();
+@@ -1829,6 +1915,596 @@
+ 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;
++ return abs((int) lim->ha.netaddr.s_addr);
++}
++
++static int addr_range_cmp_cb(void *obj, void *arg, int flags)
++{
++ struct addr_range *lim1 = obj, *lim2 = arg;
++ return ((lim1->ha.netaddr.s_addr == lim2->ha.netaddr.s_addr) &&
++ (lim1->ha.netmask.s_addr == lim2->ha.netmask.s_addr)) ?
++ 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;
++
++ if ((sin->sin_addr.s_addr & addr_range->ha.netmask.s_addr) == addr_range->ha.netaddr.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) && (peer = find_peer(find, 1))) {
++ 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 sockaddr_in *sin)
++{
++ /* this function turns off and on custom callno limits set by peer registration */
++ struct peercnt *peercnt;
++ struct peercnt 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 (optional)>\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);
++ }
++
++ if (a->argc == 4) {
++ ast_cli(a->fd, "\nNon-CallToken Validation Limit: %d\nNon-CallToken Validated: %d\n", global_maxcallno_nonval, total_nonval_callno_used);
++ } 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.
+ */
+@@ -1836,7 +2512,9 @@
+ {
+ int res = 0;
+ int x;
+- struct timeval now;
++ /* 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) {
+@@ -1928,8 +2606,7 @@
+ #endif
+ }
+ if (!res && (new >= NEW_ALLOW)) {
+- int start, found = 0;
+-
++ 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,
+@@ -1939,36 +2616,28 @@
+ if (!iax2_getpeername(*sin, host, sizeof(host)))
+ snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
+
+- now = ast_tvnow();
+- start = 2 + (ast_random() % (TRUNK_CALL_START - 1));
+- for (x = start; 1; x++) {
+- if (x == TRUNK_CALL_START) {
+- x = 1;
+- continue;
+- }
++ 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;
++ }
+
+- /* Find first unused call number that hasn't been used in a while */
+- ast_mutex_lock(&iaxsl[x]);
+- if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) {
+- found = 1;
+- break;
+- }
+- ast_mutex_unlock(&iaxsl[x]);
+-
+- if (x == start - 1) {
+- break;
+- }
+- }
+- /* We've still got lock held if we found a spot */
+- if (x == start - 1 && !found) {
++ 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;
+@@ -1992,6 +2661,7 @@
+ } else {
+ ast_log(LOG_WARNING, "Out of resources\n");
+ ast_mutex_unlock(&iaxsl[x]);
++ replace_callno(callno_entry);
+ return 0;
+ }
+ if (!return_locked)
+@@ -2491,8 +3161,6 @@
+ iax2_destroy_helper(pvt);
+ }
+
+- lastused[callno] = ast_tvnow();
+-
+ owner = pvt ? pvt->owner : NULL;
+
+ if (owner) {
+@@ -2865,6 +3533,8 @@
+ ast_cli(a->fd, " Parking lot : %s\n", peer->parkinglot);
+ ast_cli(a->fd, " Mailbox : %s\n", peer->mailbox);
+ ast_cli(a->fd, " Dynamic : %s\n", ast_test_flag(peer, IAX_DYNAMIC) ? "Yes" : "No");
++ ast_cli(a->fd, " Callnum limit: %d\n", peer->maxcallno);
++ ast_cli(a->fd, " Calltoken req: %s\n", (peer->calltoken_required == CALLTOKEN_YES) ? "Yes" : ((peer->calltoken_required == CALLTOKEN_AUTO) ? "Auto" : "No"));
+ ast_cli(a->fd, " Trunk : %s\n", ast_test_flag(peer, IAX_TRUNK) ? "Yes" : "No");
+ ast_cli(a->fd, " Encryption : %s\n", peer->encmethods ? ast_str_buffer(encmethods) : "No");
+ ast_cli(a->fd, " Callerid : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, "<unspecified>"));
+@@ -3733,16 +4403,250 @@
+
+ static int send_apathetic_reply(unsigned short callno, unsigned short dcallno,
+ struct sockaddr_in *sin, int command, int ts, unsigned char seqno,
+- int sockfd)
++ int sockfd, struct iax_ie_data *ied)
+ {
+- struct ast_iax2_full_hdr f = { .scallno = htons(0x8000 | callno), .dcallno = htons(dcallno),
+- .ts = htonl(ts), .iseqno = seqno, .oseqno = 0, .type = AST_FRAME_IAX,
+- .csub = compress_subclass(command) };
++ struct {
++ struct ast_iax2_full_hdr f;
++ struct iax_ie_data ied;
++ } data;
++ size_t size = sizeof(struct ast_iax2_full_hdr);
+
+- return sendto(sockfd, &f, sizeof(f), 0, (struct sockaddr *)sin, sizeof(*sin));
++ if (ied) {
++ size += ied->pos;
++ memcpy(&data.ied, ied->buf, ied->pos);
++ }
++
++ data.f.scallno = htons(0x8000 | callno);
++ data.f.dcallno = htons(dcallno);
++ data.f.ts = htonl(ts);
++ data.f.iseqno = seqno;
++ data.f.oseqno = 0;
++ data.f.type = AST_FRAME_IAX;
++ data.f.csub = compress_subclass(command);
++
++ return sendto(sockfd, &data, size, 0, (struct sockaddr *)sin, sizeof(*sin));
+ }
+
++static void add_empty_calltoken_ie(struct chan_iax2_pvt *pvt, struct iax_ie_data *ied)
++{
++ /* first make sure their are two empty bytes left in ied->buf */
++ if (pvt && ied && (2 < ((int) sizeof(ied->buf) - ied->pos))) {
++ ied->buf[ied->pos++] = IAX_IE_CALLTOKEN; /* type */
++ ied->buf[ied->pos++] = 0; /* data size, ZERO in this case */
++ pvt->calltoken_ie_len = 2;
++ }
++}
++
++static void resend_with_token(int callno, struct iax_frame *f, const char *newtoken)
++{
++ struct chan_iax2_pvt *pvt = iaxs[callno];
++ int frametype = f->af.frametype;
++ int subclass = f->af.subclass;
++ struct {
++ struct ast_iax2_full_hdr fh;
++ struct iax_ie_data ied;
++ } data = {
++ .ied.buf = { 0 },
++ .ied.pos = 0,
++ };
++ /* total len - header len gives us the frame's IE len */
++ int ie_data_pos = f->datalen - sizeof(struct ast_iax2_full_hdr);
++
++ if (!pvt) {
++ return; /* this should not be possible if called from socket_process() */
++ }
++
++ /*
++ * Check to make sure last frame sent is valid for call token resend
++ * 1. Frame should _NOT_ be encrypted since it starts the IAX dialog
++ * 2. Frame should _NOT_ already have a destination callno
++ * 3. Frame must be a valid iax_frame subclass capable of starting dialog
++ * 4. Pvt must have a calltoken_ie_len which represents the number of
++ * bytes at the end of the frame used for the previous calltoken ie.
++ * 5. Pvt's calltoken_ie_len must be _LESS_ than the total IE length
++ * 6. Total length of f->data must be _LESS_ than size of our data struct
++ * because f->data must be able to fit within data.
++ */
++ if (f->encmethods || f->dcallno || !iax2_allow_new(frametype, subclass, 0)
++ || !pvt->calltoken_ie_len || (pvt->calltoken_ie_len > ie_data_pos) ||
++ (f->datalen > sizeof(data))) {
++
++ return; /* ignore resend, token was not valid for the dialog */
++ }
++
++ /* token is valid
++ * 1. Copy frame data over
++ * 2. Redo calltoken IE, it will always be the last ie in the frame.
++ * NOTE: Having the ie always be last is not protocol specified,
++ * it is only an implementation choice. Since we only expect the ie to
++ * be last for frames we have sent, this can no way be affected by
++ * another end point.
++ * 3. Remove frame from queue
++ * 4. Free old frame
++ * 5. Clear previous seqnos
++ * 6. Resend with CALLTOKEN ie.
++ */
++
++ /* ---1.--- */
++ memcpy(&data, f->data, f->datalen);
++ data.ied.pos = ie_data_pos;
++
++ /* ---2.--- */
++ /* move to the beginning of the calltoken ie so we can write over it */
++ data.ied.pos -= pvt->calltoken_ie_len;
++ iax_ie_append_str(&data.ied, IAX_IE_CALLTOKEN, newtoken);
++
++ /* make sure to update token length incase it ever has to be stripped off again */
++ pvt->calltoken_ie_len = data.ied.pos - ie_data_pos; /* new pos minus old pos tells how big token ie is */
++
++ /* ---3.--- */
++ AST_LIST_LOCK(&frame_queue);
++ AST_LIST_REMOVE(&frame_queue, f, list);
++ AST_LIST_UNLOCK(&frame_queue);
++
++ /* ---4.--- */
++ iax2_frame_free(f);
++
++ /* ---5.--- */
++ pvt->oseqno = 0;
++ pvt->rseqno = 0;
++ pvt->iseqno = 0;
++ pvt->aseqno = 0;
++ if (pvt->peercallno) {
++ remove_by_peercallno(pvt);
++ pvt->peercallno = 0;
++ }
++
++ /* ---6.--- */
++ send_command(pvt, AST_FRAME_IAX, subclass, 0, data.ied.buf, data.ied.pos, -1);
++}
++
++static void requirecalltoken_mark_auto(const char *name, int subclass)
++{
++ struct iax2_user *user = NULL;
++ struct iax2_peer *peer = NULL;
++
++ if (ast_strlen_zero(name)) {
++ return; /* no username given */
++ }
++
++ if ((subclass == IAX_COMMAND_NEW) && (user = find_user(name)) && (user->calltoken_required == CALLTOKEN_AUTO)) {
++ user->calltoken_required = CALLTOKEN_YES;
++ } else if ((subclass != IAX_COMMAND_NEW) && (peer = find_peer(name, 1)) && (peer->calltoken_required == CALLTOKEN_AUTO)) {
++ peer->calltoken_required = CALLTOKEN_YES;
++ }
++
++ if (peer) {
++ peer_unref(peer);
++ }
++ if (user) {
++ user_unref(user);
++ }
++}
++
+ /*!
++ * \internal
++ *
++ * \brief handles calltoken logic for a received iax_frame.
++ *
++ * \note frametype must be AST_FRAME_IAX.
++ *
++ * \note
++ * Three different cases are possible here.
++ * Case 1. An empty calltoken is provided. This means the client supports
++ * calltokens but has not yet received one from us. In this case
++ * a full calltoken IE is created and sent in a calltoken fullframe.
++ * Case 2. A full calltoken is received and must be checked for validity.
++ * Case 3. No calltoken is received indicating that the client does not
++ * support calltokens. In this case it is up to the configuration
++ * to decide how this should be handled (reject or permit without calltoken)
++ */
++static int handle_call_token(struct ast_iax2_full_hdr *fh, struct iax_ies *ies,
++ struct sockaddr_in *sin, int fd)
++{
++#define CALLTOKEN_HASH_FORMAT "%s%d%u%d" /* address + port + ts + randomcalldata */
++#define CALLTOKEN_IE_FORMAT "%u?%s" /* time + ? + (40 char hash) */
++ struct ast_str *buf = ast_str_alloca(256);
++ time_t t = time(NULL);
++ char hash[41]; /* 40 char sha1 hash */
++ int subclass = uncompress_subclass(fh->csub);
++
++ /* ----- Case 1 ----- */
++ if (ies->calltoken && !ies->calltokendata) { /* empty calltoken is provided, client supports calltokens */
++ struct iax_ie_data ied = {
++ .buf = { 0 },
++ .pos = 0,
++ };
++
++ /* create the hash with their address data and our timestamp */
++ ast_str_set(&buf, 0, CALLTOKEN_HASH_FORMAT, ast_inet_ntoa(sin->sin_addr), sin->sin_port, (unsigned int) t, randomcalltokendata);
++ ast_sha1_hash(hash, ast_str_buffer(buf));
++
++ ast_str_set(&buf, 0, CALLTOKEN_IE_FORMAT, (unsigned int) t, hash);
++ iax_ie_append_str(&ied, IAX_IE_CALLTOKEN, ast_str_buffer(buf));
++ send_apathetic_reply(1, ntohs(fh->scallno), sin, IAX_COMMAND_CALLTOKEN, ntohl(fh->ts), fh->iseqno + 1, fd, &ied);
++
++ return 1;
++
++ /* ----- Case 2 ----- */
++ } else if (ies->calltoken && ies->calltokendata) { /* calltoken received, check to see if it is valid */
++ char *rec_hash = NULL; /* the received hash, make sure it matches with ours. */
++ char *rec_ts = NULL; /* received timestamp */
++ unsigned int rec_time; /* received time_t */
++
++ /* split the timestamp from the hash data */
++ rec_hash = strchr((char *) ies->calltokendata, '?');
++ if (rec_hash) {
++ *rec_hash++ = '\0';
++ rec_ts = (char *) ies->calltokendata;
++ }
++
++ /* check that we have valid data before we do any comparisons */
++ if (!rec_hash || !rec_ts) {
++ goto reject;
++ } else if (sscanf(rec_ts, "%u", &rec_time) != 1) {
++ goto reject;
++ }
++
++ /* create a hash with their address and the _TOKEN'S_ timestamp */
++ ast_str_set(&buf, 0, CALLTOKEN_HASH_FORMAT, ast_inet_ntoa(sin->sin_addr), sin->sin_port, (unsigned int) rec_time, randomcalltokendata);
++ ast_sha1_hash(hash, ast_str_buffer(buf));
++
++ /* compare hashes and then check timestamp delay */
++ if (strcmp(hash, rec_hash)) {
++ ast_log(LOG_WARNING, "Address %s failed CallToken hash inspection\n", ast_inet_ntoa(sin->sin_addr));
++ goto reject; /* received hash does not match ours, reject */
++ } else if ((t < rec_time) || ((t - rec_time) >= MAX_CALLTOKEN_DELAY)) {
++ ast_log(LOG_WARNING, "Too much delay in IAX2 calltoken timestamp from address %s\n", ast_inet_ntoa(sin->sin_addr));
++ goto reject; /* too much delay, reject */
++ }
++
++ /* at this point the call token is valid, returning 0
++ * will allow socket_process to continue as usual */
++ requirecalltoken_mark_auto(ies->username, subclass);
++ return 0;
++
++ /* ----- Case 3 ----- */
++ } else { /* calltokens are not supported for this client, how do we respond? */
++ if (calltoken_required(sin, ies->username, subclass)) {
++ ast_log(LOG_ERROR, "Call rejected, CallToken Support required. If unexpected, resolve by placing address %s in the calltokenignore list or setting user %s requirecalltoken=no\n", ast_inet_ntoa(sin->sin_addr), ies->username);
++ goto reject;
++ }
++ return 0; /* calltoken is not required for this addr, so permit it. */
++ }
++
++reject:
++ /* received frame has failed calltoken inspection, send apathetic reject messages */
++ if (subclass == IAX_COMMAND_REGREQ || subclass == IAX_COMMAND_REGREL) {
++ send_apathetic_reply(1, ntohs(fh->scallno), sin, IAX_COMMAND_REGREJ, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
++ } else {
++ send_apathetic_reply(1, ntohs(fh->scallno), sin, IAX_COMMAND_REJECT, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
++ }
++
++ return 1;
++}
++
++/*!
+ * \brief Parses an IAX dial string into its component parts.
+ * \param data the string to be parsed
+ * \param pds pointer to a \c struct \c parsed_dial_string to be filled in
+@@ -3984,6 +4888,7 @@
+ }
+
+ /* Transmit the string in a "NEW" request */
++ add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */
+ send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1);
+
+ ast_mutex_unlock(&iaxsl[callno]);
+@@ -7044,6 +7949,12 @@
+ return -1;
+ }
+ remove_by_transfercallno(pvt);
++ /* since a transfer has taken place, the address will change.
++ * This must be accounted for in the peercnts table. Remove
++ * the old address and add the new one */
++ peercnt_remove_by_addr(&pvt->addr);
++ peercnt_add(&pvt->transfer);
++ /* now copy over the new address */
+ memcpy(&pvt->addr, &pvt->transfer, sizeof(pvt->addr));
+ memset(&pvt->transfer, 0, sizeof(pvt->transfer));
+ /* Reset sequence numbers */
+@@ -7264,6 +8175,8 @@
+ if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(peer, IAX_TEMPONLY|IAX_RTCACHEFRIENDS)))
+ realtime_update_peer(peer->name, &peer->addr, 0);
+ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
++ /* modify entry in peercnts table as _not_ registered */
++ peercnt_modify(0, 0, &peer->addr);
+ /* Reset the address */
+ memset(&peer->addr, 0, sizeof(peer->addr));
+ /* Reset expiry value */
+@@ -7379,8 +8292,13 @@
+ if (inaddrcmp(&p->addr, sin)) {
+ if (iax2_regfunk)
+ iax2_regfunk(p->name, 1);
++
++ /* modify entry in peercnts table as _not_ registered */
++ peercnt_modify(0, 0, &p->addr);
++
+ /* Stash the IP address from which they registered */
+ memcpy(&p->addr, sin, sizeof(p->addr));
++
+ snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), p->expiry);
+ if (!ast_test_flag(p, IAX_TEMPONLY) && sin->sin_addr.s_addr) {
+ ast_db_put("IAX/Registry", p->name, data);
+@@ -7400,8 +8318,13 @@
+ /* Update the host */
+ /* Verify that the host is really there */
+ iax2_poke_peer(p, callno);
+- }
++ }
+
++ /* modify entry in peercnts table as registered */
++ if (p->maxcallno) {
++ peercnt_modify(1, p->maxcallno, &p->addr);
++ }
++
+ /* Make sure our call still exists, an INVAL at the right point may make it go away */
+ if (!iaxs[callno]) {
+ res = -1;
+@@ -7576,6 +8499,7 @@
+ res = authenticate(challenge, reg->secret, NULL, authmethods, &ied, sin, NULL);
+ if (!res) {
+ reg->regstate = REG_STATE_AUTHSENT;
++ add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */
+ return send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1);
+ } else
+ return -1;
+@@ -8521,17 +9445,50 @@
+ /* Deal with POKE/PONG without allocating a callno */
+ if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_POKE) {
+ /* Reply back with a PONG, but don't care about the result. */
+- send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_PONG, ntohl(fh->ts), fh->iseqno + 1, fd);
++ send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_PONG, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
+ return 1;
+ } else if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_ACK && dcallno == 1) {
+ /* Ignore */
+ return 1;
+ }
+
+- if ((f.frametype == AST_FRAME_IAX) && ((f.subclass == IAX_COMMAND_NEW) || (f.subclass == IAX_COMMAND_REGREQ) ||
+- (f.subclass == IAX_COMMAND_POKE) || (f.subclass == IAX_COMMAND_FWDOWNL) ||
+- (f.subclass == IAX_COMMAND_REGREL)))
+- new = NEW_ALLOW;
++ f.datalen = res - sizeof(*fh);
++ if (f.datalen) {
++ if (f.frametype == AST_FRAME_IAX) {
++ if (iax_parse_ies(&ies, thread->buf + sizeof(struct ast_iax2_full_hdr), f.datalen)) {
++ ast_log(LOG_WARNING, "Undecodable frame received from '%s'\n", ast_inet_ntoa(sin.sin_addr));
++ return 1;
++ }
++ f.data.ptr = NULL;
++ f.datalen = 0;
++ } else {
++ f.data.ptr = thread->buf + sizeof(struct ast_iax2_full_hdr);
++ memset(&ies, 0, sizeof(ies));
++ }
++ } else {
++ if (f.frametype == AST_FRAME_IAX)
++ f.data.ptr = NULL;
++ else
++ f.data.ptr = empty;
++ memset(&ies, 0, sizeof(ies));
++ }
++
++ if (!dcallno && iax2_allow_new(f.frametype, f.subclass, 1)) {
++ /* only set NEW_ALLOW if calltoken checks out */
++ if (handle_call_token(fh, &ies, &sin, fd)) {
++ return 1;
++ }
++
++ if (ies.calltoken && ies.calltokendata) {
++ /* if we've gotten this far, and the calltoken ie data exists,
++ * then calltoken validation _MUST_ have taken place. If calltoken
++ * data is provided, it is always validated reguardless of any
++ * calltokenoptional or requirecalltoken options */
++ new = NEW_ALLOW_CALLTOKEN_VALIDATED;
++ } else {
++ new = NEW_ALLOW;
++ }
++ }
+ } else {
+ /* Don't know anything about it yet */
+ f.frametype = AST_FRAME_NULL;
+@@ -8557,7 +9514,14 @@
+ check_dcallno = f.frametype == AST_FRAME_IAX ? (f.subclass != IAX_COMMAND_PING && f.subclass != IAX_COMMAND_LAGRQ) : 1;
+ }
+
+- fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, fd, check_dcallno);
++ if (!(fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, fd, check_dcallno))) {
++ if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_NEW) {
++ send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_REJECT, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
++ } else if (f.frametype == AST_FRAME_IAX && (f.subclass == IAX_COMMAND_REGREQ || f.subclass == IAX_COMMAND_REGREL)) {
++ send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_REGREJ, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
++ }
++ return 1;
++ }
+ }
+
+ if (fr->callno > 0)
+@@ -8681,12 +9645,6 @@
+ (f.frametype != AST_FRAME_IAX))
+ iaxs[fr->callno]->iseqno++;
+ }
+- /* A full frame */
+- if (res < sizeof(*fh)) {
+- ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int) sizeof(*fh));
+- ast_mutex_unlock(&iaxsl[fr->callno]);
+- return 1;
+- }
+ /* Ensure text frames are NULL-terminated */
+ if (f.frametype == AST_FRAME_TEXT && thread->buf[res - 1] != '\0') {
+ if (res < thread->buf_size)
+@@ -8694,7 +9652,6 @@
+ else /* Trims one character from the text message, but that's better than overwriting the end of the buffer. */
+ thread->buf[res - 1] = '\0';
+ }
+- f.datalen = res - sizeof(*fh);
+
+ /* Handle implicit ACKing unless this is an INVAL, and only if this is
+ from the real peer, not the transfer peer */
+@@ -8756,27 +9713,6 @@
+ return 1;
+ }
+
+- if (f.datalen) {
+- if (f.frametype == AST_FRAME_IAX) {
+- if (iax_parse_ies(&ies, thread->buf + sizeof(*fh), f.datalen)) {
+- ast_log(LOG_WARNING, "Undecodable frame received from '%s'\n", ast_inet_ntoa(sin.sin_addr));
+- ast_mutex_unlock(&iaxsl[fr->callno]);
+- return 1;
+- }
+- f.data.ptr = NULL;
+- f.datalen = 0;
+- } else {
+- f.data.ptr = thread->buf + sizeof(*fh);
+- memset(&ies, 0, sizeof(ies));
+- }
+- } else {
+- if (f.frametype == AST_FRAME_IAX)
+- f.data.ptr = NULL;
+- else
+- f.data.ptr = empty;
+- memset(&ies, 0, sizeof(ies));
+- }
+-
+ /* when we receive the first full frame for a new incoming channel,
+ it is safe to start the PBX on the channel because we have now
+ completed a 3-way handshake with the peer */
+@@ -9926,6 +10862,28 @@
+ return 1;
+ }
+ break;
++ case IAX_COMMAND_CALLTOKEN:
++ {
++ struct iax_frame *cur;
++ int found = 0;
++ AST_LIST_LOCK(&frame_queue);
++ AST_LIST_TRAVERSE(&frame_queue, cur, list) {
++ /* find the last sent frame in our frame queue for this callno.
++ * There are many things to take into account before resending this frame.
++ * All of these are taken care of in resend_with_token() */
++ if (cur->callno == fr->callno) {
++ found = 1;
++ break;
++ }
++ }
++ AST_LIST_UNLOCK(&frame_queue);
++
++ /* find last sent frame */
++ if (cur && found && ies.calltoken && ies.calltokendata) {
++ resend_with_token(fr->callno, cur, (char *) ies.calltokendata);
++ }
++ break;
++ }
+ default:
+ ast_debug(1, "Unknown IAX command %d on %d/%d\n", f.subclass, fr->callno, iaxs[fr->callno]->peercallno);
+ memset(&ied0, 0, sizeof(ied0));
+@@ -10241,6 +11199,7 @@
+ memset(&ied, 0, sizeof(ied));
+ iax_ie_append_str(&ied, IAX_IE_USERNAME, reg->username);
+ iax_ie_append_short(&ied, IAX_IE_REFRESH, reg->refresh);
++ add_empty_calltoken_ie(iaxs[reg->callno], &ied); /* this _MUST_ be the last ie added */
+ send_command(iaxs[reg->callno],AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1);
+ reg->regstate = REG_STATE_REGSENT;
+ return 0;
+@@ -10464,7 +11423,12 @@
+ /* And send the poke */
+ ast_mutex_lock(&iaxsl[callno]);
+ if (iaxs[callno]) {
+- send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_POKE, 0, NULL, 0, -1);
++ struct iax_ie_data ied = {
++ .buf = { 0 },
++ .pos = 0,
++ };
++ add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */
++ send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_POKE, 0, ied.buf, ied.pos, -1);
+ }
+ ast_mutex_unlock(&iaxsl[callno]);
+
+@@ -10848,6 +11812,9 @@
+ peer->smoothing = 0;
+ peer->pokefreqok = DEFAULT_FREQ_OK;
+ peer->pokefreqnotok = DEFAULT_FREQ_NOTOK;
++ peer->maxcallno = 0;
++ peercnt_modify(0, 0, &peer->addr);
++ peer->calltoken_required = CALLTOKEN_DEFAULT;
+ ast_string_field_set(peer,context,"");
+ ast_string_field_set(peer,peercontext,"");
+ ast_clear_flag(peer, IAX_HASCALLERID);
+@@ -11009,7 +11976,24 @@
+ ast_string_field_set(peer, zonetag, v->value);
+ } else if (!strcasecmp(v->name, "adsi")) {
+ peer->adsi = ast_true(v->value);
+- }/* else if (strcasecmp(v->name,"type")) */
++ } else if (!strcasecmp(v->name, "maxcallnumbers")) {
++ if (sscanf(v->value, "%10hu", &peer->maxcallno) != 1) {
++ ast_log(LOG_WARNING, "maxcallnumbers must be set to a valid number. %s is not valid at line %d.\n", v->value, v->lineno);
++ } else {
++ peercnt_modify(1, peer->maxcallno, &peer->addr);
++ }
++ } else if (!strcasecmp(v->name, "requirecalltoken")) {
++ /* default is required unless in optional ip list */
++ if (ast_false(v->value)) {
++ peer->calltoken_required = CALLTOKEN_NO;
++ } else if (!strcasecmp(v->value, "auto")) {
++ peer->calltoken_required = CALLTOKEN_AUTO;
++ } else if (ast_true(v->value)) {
++ peer->calltoken_required = CALLTOKEN_YES;
++ } else {
++ ast_log(LOG_WARNING, "requirecalltoken must be set to a valid value. at line %d\n", v->lineno);
++ }
++ } /* else if (strcasecmp(v->name,"type")) */
+ /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
+ v = v->next;
+ if (!v) {
+@@ -11105,6 +12089,7 @@
+ user->capability = iax2_capability;
+ user->encmethods = iax2_encryption;
+ user->adsi = adsi;
++ user->calltoken_required = CALLTOKEN_DEFAULT;
+ ast_string_field_set(user, name, name);
+ ast_string_field_set(user, language, language);
+ ast_copy_flags(user, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_CODEC_USER_FIRST | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP | IAX_FORCE_ENCRYPT);
+@@ -11255,7 +12240,18 @@
+ user->maxauthreq = 0;
+ } else if (!strcasecmp(v->name, "adsi")) {
+ user->adsi = ast_true(v->value);
+- }/* else if (strcasecmp(v->name,"type")) */
++ } else if (!strcasecmp(v->name, "requirecalltoken")) {
++ /* default is required unless in optional ip list */
++ if (ast_false(v->value)) {
++ user->calltoken_required = CALLTOKEN_NO;
++ } else if (!strcasecmp(v->value, "auto")) {
++ user->calltoken_required = CALLTOKEN_AUTO;
++ } else if (ast_true(v->value)) {
++ user->calltoken_required = CALLTOKEN_YES;
++ } else {
++ ast_log(LOG_WARNING, "requirecalltoken must be set to a valid value. at line %d\n", v->lineno);
++ }
++ } /* else if (strcasecmp(v->name,"type")) */
+ /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
+ v = v->next;
+ if (!v) {
+@@ -11374,10 +12370,12 @@
+ ast_clear_flag((&globalflags), IAX_USEJITTERBUF);
+ ast_clear_flag((&globalflags), IAX_FORCEJITTERBUF);
+ delete_users();
++ ao2_callback(callno_limits, OBJ_NODATA, addr_range_delme_cb, NULL);
++ ao2_callback(calltoken_ignores, OBJ_NODATA, addr_range_delme_cb, NULL);
+ }
+
+ /*! \brief Load configuration */
+-static int set_config(char *config_file, int reload)
++static int set_config(const char *config_file, int reload)
+ {
+ struct ast_config *cfg, *ucfg;
+ int capability=iax2_capability;
+@@ -11445,6 +12443,8 @@
+ min_reg_expire = IAX_DEFAULT_REG_EXPIRE;
+ max_reg_expire = IAX_DEFAULT_REG_EXPIRE;
+ global_max_trunk_mtu = MAX_TRUNK_MTU;
++ global_maxcallno = DEFAULT_MAXCALLNO_LIMIT;
++ global_maxcallno_nonval = DEFAULT_MAXCALLNO_LIMIT_NONVAL;
+
+ maxauthreq = 3;
+
+@@ -11677,7 +12677,19 @@
+ adsi = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "srvlookup")) {
+ srvlookup = ast_true(v->value);
+- } /*else if (strcasecmp(v->name,"type")) */
++ } else if (!strcasecmp(v->name, "maxcallnumbers")) {
++ if (sscanf(v->value, "%10hu", &global_maxcallno) != 1) {
++ ast_log(LOG_WARNING, "maxcallnumbers must be set to a valid number. %s is not valid at line %d\n", v->value, v->lineno);
++ }
++ } else if (!strcasecmp(v->name, "maxcallnumbers_nonvalidated")) {
++ if (sscanf(v->value, "%10hu", &global_maxcallno_nonval) != 1) {
++ ast_log(LOG_WARNING, "maxcallnumbers_nonvalidated must be set to a valid number. %s is not valid at line %d.\n", v->value, v->lineno);
++ }
++ } else if(!strcasecmp(v->name, "calltokenoptional")) {
++ if (add_calltoken_ignore(v->value)) {
++ ast_log(LOG_WARNING, "Invalid calltokenoptional address range - '%s' line %d\n", v->value, v->lineno);
++ }
++ }/*else if (strcasecmp(v->name,"type")) */
+ /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
+ v = v->next;
+ }
+@@ -11766,7 +12778,9 @@
+ while(cat) {
+ if (strcasecmp(cat, "general")) {
+ utype = ast_variable_retrieve(cfg, cat, "type");
+- if (utype) {
++ if (!strcasecmp(cat, "callnumberlimits")) {
++ build_callno_limits(ast_variable_browse(cfg, cat));
++ } else if (utype) {
+ if (!strcasecmp(utype, "user") || !strcasecmp(utype, "friend")) {
+ user = build_user(cat, ast_variable_browse(cfg, cat), NULL, 0);
+ if (user) {
+@@ -11807,12 +12821,15 @@
+ }
+ static int reload_config(void)
+ {
+- char *config = "iax.conf";
++ static const char config[] = "iax.conf";
+ struct iax2_registry *reg;
+
+ if (set_config(config, 1) > 0) {
+ prune_peers();
+ prune_users();
++ ao2_callback(callno_limits, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, prune_addr_range_cb, NULL);
++ ao2_callback(calltoken_ignores, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, prune_addr_range_cb, NULL);
++ ao2_callback(peercnts, OBJ_NODATA, set_peercnt_limit_all_cb, NULL);
+ trunk_timed = trunk_untimed = 0;
+ trunk_nmaxmtu = trunk_maxmtu = 0;
+ memset(&debugaddr, '\0', sizeof(debugaddr));
+@@ -11923,6 +12940,7 @@
+ if (pds.key)
+ ast_string_field_set(iaxs[callno], outkey, pds.key);
+ /* Start the call going */
++ add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */
+ send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1);
+
+ return callno;
+@@ -12485,6 +13503,7 @@
+ AST_CLI_DEFINE(handle_cli_iax2_show_users, "List defined IAX users"),
+ AST_CLI_DEFINE(handle_cli_iax2_test_losspct, "Set IAX2 incoming frame loss percentage"),
+ AST_CLI_DEFINE(handle_cli_iax2_unregister, "Unregister (force expiration) an IAX2 peer from the registry"),
++ AST_CLI_DEFINE(handle_cli_iax2_show_callno_limits, "Show current entries in IP call number limit table"),
+ #ifdef IAXTESTS
+ AST_CLI_DEFINE(handle_cli_iax2_test_jitter, "Simulates jitter for testing"),
+ AST_CLI_DEFINE(handle_cli_iax2_test_late, "Test the receipt of a late frame"),
+@@ -12558,6 +13577,11 @@
+ ao2_ref(users, -1);
+ ao2_ref(iax_peercallno_pvts, -1);
+ ao2_ref(iax_transfercallno_pvts, -1);
++ ao2_ref(peercnts, -1);
++ ao2_ref(callno_limits, -1);
++ ao2_ref(calltoken_ignores, -1);
++ ao2_ref(callno_pool, -1);
++ ao2_ref(callno_pool_trunk, -1);
+ if (timer) {
+ ast_timer_close(timer);
+ }
+@@ -12621,34 +13645,78 @@
+ return match(&pvt2->transfer, pvt2->transfercallno, pvt2->callno, pvt,
+ pvt2->frames_received) ? CMP_MATCH | CMP_STOP : 0;
+ }
+-/*! \brief Load IAX2 module, load configuraiton ---*/
+-static int load_module(void)
++
++static int load_objects(void)
+ {
+- char *config = "iax.conf";
+- int x = 0;
+- struct iax2_registry *reg = NULL;
++ peers = users = iax_peercallno_pvts = iax_transfercallno_pvts = NULL;
++ peercnts = callno_limits = calltoken_ignores = callno_pool = callno_pool_trunk = NULL;
+
+- peers = ao2_container_alloc(MAX_PEER_BUCKETS, peer_hash_cb, peer_cmp_cb);
+- if (!peers)
+- return AST_MODULE_LOAD_FAILURE;
+- users = ao2_container_alloc(MAX_USER_BUCKETS, user_hash_cb, user_cmp_cb);
+- if (!users) {
++ if (!(peers = ao2_container_alloc(MAX_PEER_BUCKETS, peer_hash_cb, peer_cmp_cb))) {
++ goto container_fail;
++ } else if (!(users = ao2_container_alloc(MAX_USER_BUCKETS, user_hash_cb, user_cmp_cb))) {
++ goto container_fail;
++ } else if (!(iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb))) {
++ goto container_fail;
++ } else if (!(iax_transfercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, transfercallno_pvt_hash_cb, transfercallno_pvt_cmp_cb))) {
++ goto container_fail;
++ } else if (!(peercnts = ao2_container_alloc(MAX_PEER_BUCKETS, peercnt_hash_cb, peercnt_cmp_cb))) {
++ goto container_fail;
++ } else if (!(callno_limits = ao2_container_alloc(MAX_PEER_BUCKETS, addr_range_hash_cb, addr_range_cmp_cb))) {
++ goto container_fail;
++ } else if (!(calltoken_ignores = ao2_container_alloc(MAX_PEER_BUCKETS, addr_range_hash_cb, addr_range_cmp_cb))) {
++ goto container_fail;
++ } else if (create_callno_pools()) {
++ goto container_fail;
++ }
++
++ return 0;
++
++container_fail:
++ if (peers) {
+ ao2_ref(peers, -1);
+- return AST_MODULE_LOAD_FAILURE;
+ }
+- iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb);
+- if (!iax_peercallno_pvts) {
+- ao2_ref(peers, -1);
++ if (users) {
+ ao2_ref(users, -1);
+- return AST_MODULE_LOAD_FAILURE;
+ }
+- iax_transfercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, transfercallno_pvt_hash_cb, transfercallno_pvt_cmp_cb);
+- if (!iax_transfercallno_pvts) {
+- ao2_ref(peers, -1);
+- ao2_ref(users, -1);
++ if (iax_peercallno_pvts) {
+ ao2_ref(iax_peercallno_pvts, -1);
++ }
++ if (iax_transfercallno_pvts) {
++ ao2_ref(iax_transfercallno_pvts, -1);
++ }
++ if (peercnts) {
++ ao2_ref(peercnts, -1);
++ }
++ if (callno_limits) {
++ ao2_ref(callno_limits, -1);
++ }
++ if (calltoken_ignores) {
++ ao2_ref(calltoken_ignores, -1);
++ }
++ if (callno_pool) {
++ ao2_ref(callno_pool, -1);
++ }
++ if (callno_pool_trunk) {
++ ao2_ref(callno_pool_trunk, -1);
++ }
++ return AST_MODULE_LOAD_FAILURE;
++}
++
++
++
++/*! \brief Load IAX2 module, load configuraiton ---*/
++static int load_module(void)
++{
++
++ static const char config[] = "iax.conf";
++ int x = 0;
++ struct iax2_registry *reg = NULL;
++
++ if (load_objects()) {
+ return AST_MODULE_LOAD_FAILURE;
+ }
++
++ randomcalltokendata = ast_random();
+ ast_custom_function_register(&iaxpeer_function);
+ ast_custom_function_register(&iaxvar_function);
+
+Index: channels/iax2.h
+===================================================================
+--- a/channels/iax2.h (revision 215955)
++++ b/channels/iax2.h (working copy)
+@@ -111,6 +111,8 @@
+ IAX_COMMAND_TXMEDIA = 38,
+ /*! Command to rotate key */
+ IAX_COMMAND_RTKEY = 39,
++ /*! Call number token */
++ IAX_COMMAND_CALLTOKEN = 40,
+ };
+
+ /*! By default require re-registration once per minute */
+@@ -176,6 +178,7 @@
+ #define IAX_IE_RR_OOO 51 /*!< Frames received Out of Order u32 */
+ #define IAX_IE_VARIABLE 52 /*!< Remote variables */
+ #define IAX_IE_OSPTOKEN 53 /*!< OSP token */
++#define IAX_IE_CALLTOKEN 54 /*!< Call number security token */
+
+ #define IAX_MAX_OSPBLOCK_SIZE 254 /*!< Max OSP token block size, 255 bytes - 1 byte OSP token block index */
+ #define IAX_MAX_OSPBLOCK_NUM 4
+Index: channels/iax2-parser.c
+===================================================================
+--- a/channels/iax2-parser.c (revision 215955)
++++ b/channels/iax2-parser.c (working copy)
+@@ -533,6 +533,9 @@
+ case IAX_COMMAND_RTKEY:
+ cmd = "RTKEY ";
+ break;
++ case IAX_COMMAND_CALLTOKEN:
++ cmd = "CTOKEN ";
++ break;
+ }
+ ast_copy_string(str, cmd, len);
+ }
+@@ -1049,6 +1052,12 @@
+ errorf(tmp);
+ }
+ break;
++ case IAX_IE_CALLTOKEN:
++ if (len) {
++ ies->calltokendata = (unsigned char *) data + 2;
++ }
++ ies->calltoken = 1;
++ break;
+ default:
+ snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", iax_ie2str(ie), ie, len);
+ outputf(tmp);
+Index: include/asterisk/acl.h
+===================================================================
+--- a/include/asterisk/acl.h (revision 215955)
++++ b/include/asterisk/acl.h (working copy)
+@@ -55,6 +55,9 @@
+ /*! \brief Free host access list */
+ void ast_free_ha(struct ast_ha *ha);
+
++/*! \brief Copy ha structure */
++void ast_copy_ha(const struct ast_ha *from, struct ast_ha *to);
++
+ /*! \brief Append ACL entry to host access list. */
+ struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha *path, int *error);
+
+Index: include/asterisk/astobj2.h
+===================================================================
+--- a/include/asterisk/astobj2.h (revision 215955)
++++ b/include/asterisk/astobj2.h (working copy)
+@@ -674,6 +674,15 @@
+ * The search function is unaffected (i.e. use the one passed as
+ * argument, or match_by_addr if none specified). */
+ OBJ_POINTER = (1 << 3),
++ /*!
++ * \brief Continue if a match is not found in the hashed out bucket
++ *
++ * This flag is to be used in combination with OBJ_POINTER. This tells
++ * the ao2_callback() core to keep searching through the rest of the
++ * buckets if a match is not found in the starting bucket defined by
++ * the hash value on the argument.
++ */
++ OBJ_CONTINUE = (1 << 4),
+ };
+
+ /*!
+Index: main/acl.c
+===================================================================
+--- a/main/acl.c (revision 215955)
++++ b/main/acl.c (working copy)
+@@ -225,7 +225,7 @@
+ }
+
+ /* Copy HA structure */
+-static void ast_copy_ha(struct ast_ha *from, struct ast_ha *to)
++void ast_copy_ha(const struct ast_ha *from, struct ast_ha *to)
+ {
+ memcpy(&to->netaddr, &from->netaddr, sizeof(from->netaddr));
+ memcpy(&to->netmask, &from->netmask, sizeof(from->netmask));
+Index: main/astobj2.c
+===================================================================
+--- a/main/astobj2.c (revision 215955)
++++ b/main/astobj2.c (working copy)
+@@ -604,7 +604,7 @@
+ const enum search_flags flags, void *cb_fn, void *arg, void *data, enum ao2_callback_type type,
+ char *tag, char *file, int line, const char *funcname)
+ {
+- int i, last; /* search boundaries */
++ int i, start, last; /* search boundaries */
+ void *ret = NULL;
+ ao2_callback_fn *cb_default = NULL;
+ ao2_callback_data_fn *cb_withdata = NULL;
+@@ -641,14 +641,16 @@
+ * (this only for the time being. We need to optimize this.)
+ */
+ if ((flags & OBJ_POINTER)) /* we know hash can handle this case */
+- i = c->hash_fn(arg, flags & OBJ_POINTER) % c->n_buckets;
++ start = i = c->hash_fn(arg, flags & OBJ_POINTER) % c->n_buckets;
+ else /* don't know, let's scan all buckets */
+ i = -1; /* XXX this must be fixed later. */
+
+ /* determine the search boundaries: i..last-1 */
+ if (i < 0) {
+- i = 0;
++ start = i = 0;
+ last = c->n_buckets;
++ } else if ((flags & OBJ_CONTINUE)) {
++ last = c->n_buckets;
+ } else {
+ last = i + 1;
+ }
+@@ -715,6 +717,17 @@
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
++
++ if (ret) {
++ /* This assumes OBJ_MULTIPLE with !OBJ_NODATA is still not implemented */
++ break;
++ }
++
++ if (i == c->n_buckets - 1 && (flags & OBJ_POINTER) && (flags & OBJ_CONTINUE)) {
++ /* Move to the beginning to ensure we check every bucket */
++ i = -1;
++ last = start;
++ }
+ }
+ ao2_unlock(c);
+ return ret;
+Index: configs/iax.conf.sample
+===================================================================
+--- a/configs/iax.conf.sample (revision 215955)
++++ b/configs/iax.conf.sample (working copy)
+@@ -308,6 +308,62 @@
+ ; This can also be configured per device
+ ; Parkinglots are defined in features.conf
+
++;
++; The following two options are used to disable call token validation for the
++; purposes of interoperability with IAX2 endpoints that do not yet support it.
++;
++; Call token validation can be set as optional for a single IP address or IP
++; address range by using the 'calltokenoptional' option. 'calltokenoptional' is
++; only a global option.
++;
++;calltokenoptional=209.16.236.73/255.255.255.0
++;
++; In a peer/user/friend definition, the 'requirecalltoken' option may be used.
++; By setting 'requirecalltoken=no', call token validation becomes optional for
++; that peer/user. By setting 'requirecalltoken=auto', call token validation
++; is optional until a call token supporting peer registers successfully using
++; call token validation. This is used as an indication that from now on, we
++; can require it from this peer. So, requirecalltoken is internally set to yes.
++; By default, 'requirecalltoken=yes'.
++;
++;requirecalltoken=no
++;
++
++;
++; These options are used to limit the amount of call numbers allocated to a
++; single IP address. Before changing any of these values, it is highly encouraged
++; to read the user guide associated with these options first. In most cases, the
++; default values for these options are sufficient.
++;
++; The 'maxcallnumbers' option limits the amount of call numbers allowed for each
++; individual remote IP address. Once an IP address reaches it's call number
++; limit, no more new connections are allowed until the previous ones close. This
++; option can be used in a peer definition as well, but only takes effect for
++; the IP of a dynamic peer after it completes registration.
++;
++;maxcallnumbers=512
++;
++; The 'maxcallnumbers_nonvalidated' is used to set the combined number of call
++; numbers that can be allocated for connections where call token validation
++; has been disabled. Unlike the 'maxcallnumbers' option, this limit is not
++; separate for each individual IP address. Any connection resulting in a
++; non-call token validated call number being allocated contributes to this
++; limit. For use cases, see the call token user guide. This option's
++; default value of 8192 should be sufficient in most cases.
++;
++;maxcallnumbers_nonvalidated=1024
++;
++; The [callnumberlimits] section allows custom call number limits to be set
++; for specific IP addresses and IP address ranges. These limits take precedence
++; over the global 'maxcallnumbers' option, but may still be overridden by a
++; peer defined 'maxcallnumbers' entry. Note that these limits take effect
++; for every individual address within the range, not the range as a whole.
++;
++;[callnumberlimits]
++;10.1.1.0/255.255.255.0 = 24
++;10.1.2.0/255.255.255.0 = 32
++;
++
+ ; Guest sections for unauthenticated connection attempts. Just specify an
+ ; empty secret, or provide no secret section.
+ ;
Modified: asterisk/trunk/debian/patches/series
URL: http://svn.debian.org/wsvn/pkg-voip/asterisk/trunk/debian/patches/series?rev=7592&op=diff
==============================================================================
--- asterisk/trunk/debian/patches/series (original)
+++ asterisk/trunk/debian/patches/series Sat Sep 12 21:30:57 2009
@@ -20,3 +20,4 @@
dahdi-fxsks-hookstate
dahdi_ptmp_nt
dahdi_pri_debug_spannums
+AST-2009-006
More information about the Pkg-voip-commits
mailing list