]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Merge code associated with AST-2009-006
authorDavid Vossel <dvossel@digium.com>
Thu, 3 Sep 2009 16:57:35 +0000 (16:57 +0000)
committerDavid Vossel <dvossel@digium.com>
Thu, 3 Sep 2009 16:57:35 +0000 (16:57 +0000)
(closes issue #12912)
Reported by: rathaus
Tested by: tilghman, russell, dvossel, dbrooks

git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.2@215958 65c4cc65-6c06-0410-ace0-fbb531ad65f3

14 files changed:
Makefile
acl.c
astobj2.c
channels/chan_iax2.c
channels/iax2-parser.c
channels/iax2-parser.h
channels/iax2.h
configs/iax.conf.sample
include/asterisk/acl.h
include/asterisk/astobj2.h
include/asterisk/sha1.h [new file with mode: 0644]
include/asterisk/utils.h
sha1.c [new file with mode: 0644]
utils.c

index 0413ee64bebfc49501f2de1c7d5a551318718569..605fbb71f2bf0172090b4b82b45d018acfe23ad0 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -354,7 +354,7 @@ OBJS=io.o sched.o logger.o frame.o loader.o config.o channel.o \
        astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \
        utils.o plc.o jitterbuf.o dnsmgr.o devicestate.o \
        netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \
-       cryptostub.o astobj2.o
+       cryptostub.o astobj2.o sha1.o
 
 ifeq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/sys/poll.h),)
   OBJS+= poll.o
diff --git a/acl.c b/acl.c
index 9665f35e883cef8315680b7d06762a7984e3b6b4..87250a9939cafb5a3e1a28960131cd9acd4da1a8 100644 (file)
--- a/acl.c
+++ b/acl.c
@@ -74,14 +74,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 AST_MUTEX_DEFINE_STATIC(routeseq_lock);
 #endif
 
-struct ast_ha {
-       /* Host access rule */
-       struct in_addr netaddr;
-       struct in_addr netmask;
-       int sense;
-       struct ast_ha *next;
-};
-
 /* Default IP - if not otherwise set, don't breathe garbage */
 static struct in_addr __ourip = { 0x00000000 };
 
@@ -102,7 +94,7 @@ void ast_free_ha(struct ast_ha *ha)
 }
 
 /* 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));
@@ -141,7 +133,7 @@ struct ast_ha *ast_duplicate_ha_list(struct ast_ha *original)
        return ret;                     /* Return start of list */
 }
 
-struct ast_ha *ast_append_ha(char *sense, char *stuff, struct ast_ha *path)
+struct ast_ha *ast_append_ha(char *sense, const char *stuff, struct ast_ha *path)
 {
        struct ast_ha *ha = malloc(sizeof(struct ast_ha));
        char *nm = "255.255.255.255";
index 1f33eec3fa7f175980f4c997dd1f670b168dca6a..833a98a40f5f38f80369cd2c92dfbb001620713a 100644 (file)
--- a/astobj2.c
+++ b/astobj2.c
@@ -413,7 +413,7 @@ void *ao2_callback(struct ao2_container *c,
        const enum search_flags flags,
        ao2_callback_fn cb_fn, void *arg)
 {
-       int i, last;    /* search boundaries */
+       int i, start, last;     /* search boundaries */
        void *ret = NULL;
 
        if (INTERNAL_OBJ(c) == NULL)    /* safety check on the argument */
@@ -443,13 +443,15 @@ void *ao2_callback(struct ao2_container *c,
         * (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;
@@ -505,6 +507,17 @@ void *ao2_callback(struct ao2_container *c,
                        }
                }
                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 daaa51a77eda8774313fa46057e7b4cb41a47011..f47ca82022d040af7dce1c5f75e787829e624d49 100644 (file)
@@ -281,6 +281,20 @@ static int global_rtautoclear = 120;
 static int reload_config(void);
 static int iax2_reload(int fd, int argc, char *argv[]);
 
+/*!
+ * \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 {
        char name[80];
@@ -303,6 +317,7 @@ struct iax2_user {
        struct iax2_context *contexts;
        struct iax2_user *next;
        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 {
@@ -348,8 +363,10 @@ struct iax2_peer {
        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_ha *ha;
+       enum calltoken_peer_enum calltoken_required;        /*!< Is calltoken validation required or not, can be YES, NO, or AUTO */
        struct iax2_peer *next;
 };
 
@@ -482,6 +499,8 @@ struct chan_iax2_pvt {
        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
@@ -616,8 +635,18 @@ struct chan_iax2_pvt {
        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;
+
 static struct ast_iax2_queue {
        struct iax_frame *head;
        struct iax_frame *tail;
@@ -625,6 +654,63 @@ static struct ast_iax2_queue {
        ast_mutex_t lock;
 } iaxq;
 
+static int randomcalltokendata;
+
+static const time_t MAX_CALLTOKEN_DELAY = 10;
+
+#define MAX_PEER_BUCKETS 563
+
+/*! 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 struct ast_user_list {
        struct iax2_user *users;
        ast_mutex_t lock;
@@ -673,6 +759,7 @@ AST_MUTEX_DEFINE_STATIC(dpcache_lock);
 
 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 void destroy_peer(struct iax2_peer *peer);
 static int ast_cli_netstats(int fd, int limit_fmt);
@@ -707,7 +794,7 @@ static void jb_error_output(const char *fmt, ...)
        vsnprintf(buf, 1024, fmt, args);
        va_end(args);
 
-       ast_log(LOG_ERROR, buf);
+       ast_log(LOG_ERROR, "%s", buf);
 }
 
 static void jb_warning_output(const char *fmt, ...)
@@ -719,7 +806,7 @@ static void jb_warning_output(const char *fmt, ...)
        vsnprintf(buf, 1024, fmt, args);
        va_end(args);
 
-       ast_log(LOG_WARNING, buf);
+       ast_log(LOG_WARNING, "%s", buf);
 }
 
 static void jb_debug_output(const char *fmt, ...)
@@ -731,7 +818,7 @@ static void jb_debug_output(const char *fmt, ...)
        vsnprintf(buf, 1024, fmt, args);
        va_end(args);
 
-       ast_verbose(buf);
+       ast_verbose("%s", buf);
 }
 #endif
 
@@ -739,7 +826,6 @@ static void jb_debug_output(const char *fmt, ...)
 /* XXX We probably should use a mutex when working with this XXX */
 static struct chan_iax2_pvt *iaxs[IAX_MAX_CALLS];
 static ast_mutex_t iaxsl[IAX_MAX_CALLS];
-static struct timeval lastused[IAX_MAX_CALLS];
 
 /*!
  * \brief Another container of iax2_pvt structures
@@ -782,6 +868,9 @@ static int iax2_setoption(struct ast_channel *c, int option, void *data, int dat
 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 iax2_transfer(struct ast_channel *c, const char *dest);
 static int iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan);
+static struct callno_entry *get_unused_callno(int trunk, int validated);
+static int replace_callno(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 = channeltype,
@@ -905,6 +994,21 @@ static int uncompress_subclass(unsigned char csub)
                return csub;
 }
 
+static struct iax2_user *find_user(const char *name, int realtime) 
+{
+       struct iax2_user *user;
+       ast_mutex_lock(&userl.lock);
+       for(user = userl.users; user; user = user->next) {
+               if (!strcasecmp(user->name, name)) {
+                       break;
+               }
+       }
+       ast_mutex_unlock(&userl.lock);
+       if(!user && realtime)
+               user = realtime_user(name, NULL);
+       return user;
+}
+
 static struct iax2_peer *find_peer(const char *name, int realtime) 
 {
        struct iax2_peer *peer;
@@ -1036,7 +1140,6 @@ static void iax2_destroy(int callno)
 retry:
        ast_mutex_lock(&iaxsl[callno]);
        pvt = iaxs[callno];
-       gettimeofday(&lastused[callno], NULL);
 
        if (pvt)
                owner = pvt->owner;
@@ -1096,6 +1199,9 @@ static void pvt_destructor(void *obj)
 
        iax2_destroy_helper(pvt);
 
+       sched_delay_remove(&pvt->addr, pvt->callno_entry);
+       pvt->callno_entry = NULL;
+
        if (pvt->bridgetrans)
                ast_translator_free_path(pvt->bridgetrans);
        pvt->bridgetrans = NULL;
@@ -1173,9 +1279,19 @@ static struct iax_frame *iaxfrdup2(struct iax_frame *fr)
        return new;
 }
 
-#define NEW_PREVENT    0
-#define NEW_ALLOW      1
-#define NEW_FORCE      2
+/* 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, struct chan_iax2_pvt *cur, int check_dcallno)
 {
@@ -1215,7 +1331,7 @@ static int make_trunk(unsigned short callno, int locked)
 {
        int x;
        int res= 0;
-       struct timeval now;
+       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;
@@ -1224,46 +1340,642 @@ static int make_trunk(unsigned short callno, int locked)
                ast_log(LOG_WARNING, "Call %d is already a trunk\n", callno);
                return -1;
        }
-       gettimeofday(&now, NULL);
-       for (x=TRUNK_CALL_START;x<IAX_MAX_CALLS - 1; x++) {
-               ast_mutex_lock(&iaxsl[x]);
-               if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) {
-                       iaxs[x] = iaxs[callno];
-                       iaxs[x]->callno = x;
-                       iaxs[callno] = NULL;
-                       /* Update the two timers that should have been started */
-                       if (iaxs[x]->pingid > -1)
-                               ast_sched_del(sched, iaxs[x]->pingid);
-                       if (iaxs[x]->lagid > -1)
-                               ast_sched_del(sched, iaxs[x]->lagid);
-                       iaxs[x]->pingid = ast_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x);
-                       iaxs[x]->lagid = ast_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x);
-                       if (locked)
-                               ast_mutex_unlock(&iaxsl[callno]);
-                       res = x;
-                       if (!locked)
-                               ast_mutex_unlock(&iaxsl[x]);
-                       break;
-               }
-               ast_mutex_unlock(&iaxsl[x]);
-       }
-       if (x >= IAX_MAX_CALLS - 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;
        }
-       ast_log(LOG_DEBUG, "Made call %d into trunk call %d\n", callno, x);
+       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.
+        */
+       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) {
+               ast_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 */
+       if (iaxs[x]->pingid > -1)
+               ast_sched_del(sched, iaxs[x]->pingid);
+       if (iaxs[x]->lagid > -1)
+               ast_sched_del(sched, iaxs[x]->lagid);
+       iaxs[x]->pingid = ast_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x);
+       iaxs[x]->lagid = ast_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x);
+       if (locked)
+               ast_mutex_unlock(&iaxsl[callno]);
+       res = x;
+       if (!locked)
+               ast_mutex_unlock(&iaxsl[x]);
+
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Made call %d into trunk call %d\n", callno, x);
+
        /* We move this call from a non-trunked to a trunked call */
        update_max_trunk();
        update_max_nontrunk();
        return res;
 }
 
+static int 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 = ast_strlen_zero(name) ?  "guest" : name;
+       int res = 1;  /* required by default */
+       int optional = 0;
+       enum calltoken_peer_enum calltoken_required = CALLTOKEN_DEFAULT;
+       char iabuf[INET_ADDRSTRLEN];
+       /* 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, 1))) {
+               calltoken_required = user->calltoken_required;
+       } else if ((subclass != IAX_COMMAND_NEW) && (peer = find_peer(find, 1))) {
+               calltoken_required = peer->calltoken_required;
+       }
+
+       if (option_debug) {
+               ast_log(LOG_DEBUG, "Determining if address %s with username %s requires calltoken validation.  Optional = %d  calltoken_required = %d \n", ast_inet_ntoa(iabuf, sizeof(iabuf), 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,
+       };
+       char iabuf[INET_ADDRSTRLEN];
+
+
+       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;
+               if (option_debug) {
+                       ast_log(LOG_NOTICE, "custom addr_range %d found for %s\n", limit, ast_inet_ntoa(iabuf, sizeof(iabuf), 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);
+       if (option_debug) {
+               ast_log(LOG_NOTICE, "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,
+       };
+       char iabuf[INET_ADDRSTRLEN];
+
+       if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
+               peercnt->reg = reg;
+               if (limit) {
+                       peercnt->limit = limit;
+               } else {
+                       set_peercnt_limit(peercnt);
+               }
+               if (option_debug) {
+                       ast_log(LOG_NOTICE, "peercnt entry %s modified limit:%d registered:%d", ast_inet_ntoa(iabuf, sizeof(iabuf), 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,
+       };
+       char iabuf[INET_ADDRSTRLEN];
+
+       /* 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++;
+               if (option_debug) {
+                       ast_log(LOG_NOTICE, "ip callno count incremented to %d for %s\n", peercnt->cur, ast_inet_ntoa(iabuf, sizeof(iabuf), 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(iabuf, sizeof(iabuf), 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,
+       };
+       char iabuf[INET_ADDRSTRLEN];
+
+       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--;
+               if (option_debug) {
+                       ast_log(LOG_NOTICE, "ip callno count decremented to %d for %s\n", peercnt->cur, ast_inet_ntoa(iabuf, sizeof(iabuf), 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(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 found;
+
+       for (; v; v = v->next) {
+               limit = -1;
+               found = 0;
+               ha = ast_append_ha("permit", v->name, NULL);
+
+               /* check for valid config information */
+               if (!ha) {
+                       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;
+
+       if (ast_strlen_zero(addr)) {
+               ast_log(LOG_WARNING, "invalid calltokenoptional %s\n", addr);
+               return -1;
+       }
+
+       ha = ast_append_ha("permit", addr, NULL);
+
+       /* check for valid config information */
+       if (!ha) {
+               ast_log(LOG_WARNING, "Error creating calltokenoptional entry %s\n", 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 int iax2_show_callnumber_usage(int fd, int argc, char *argv[])
+{
+       struct ao2_iterator i;
+       struct peercnt *peercnt;
+       struct sockaddr_in sin;
+       int found = 0;
+       char iabuf[INET_ADDRSTRLEN];
+
+       if (argc < 4 || argc > 5)
+               return RESULT_SHOWUSAGE;
+
+       ast_cli(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 (argc == 5 && (!strcasecmp(argv[4], ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr)))) {
+                               ast_cli(fd, "%-15s %-12d %-12d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), peercnt->cur, peercnt->limit);
+                               found = 1;
+                               break;
+               } else {
+                       ast_cli(fd, "%-15s %-12d %-12d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), peercnt->cur, peercnt->limit);
+               }
+               ao2_ref(peercnt, -1);
+       }
+       if (argc == 4) {
+               ast_cli(fd, "\nNon-CallToken Validation Limit: %d\nNon-CallToken Validated: %d\n", global_maxcallno_nonval, total_nonval_callno_used);
+       } else if (argc == 5 && !found) {
+               ast_cli(fd, "No callnumber table entries for %s found\n", argv[4] );
+       }
+       return RESULT_SUCCESS;
+}
+
+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(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(rand());
+}
+
+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,
+       };
+       char iabuf[INET_ADDRSTRLEN];
+
+       if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
+               /* refcount is incremented with ao2_find.  keep that ref for the scheduler */
+               if (option_debug) {
+                       ast_log(LOG_NOTICE, "schedule decrement of callno used for %s in %d seconds\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), MIN_REUSE_TIME);
+               }
+               i = ast_sched_add(sched, MIN_REUSE_TIME * 1000, peercnt_remove_cb, peercnt);
+               if (i == -1) {
+                       ao2_ref(peercnt, -1);
+               }
+       }
+
+       ast_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 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;
+}
+
 static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int lockpeer, int sockfd, int check_dcallno)
 {
        int res = 0;
        int x;
-       struct timeval now;
        char iabuf[INET_ADDRSTRLEN];
+       /* 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) {
@@ -1308,8 +2020,7 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc
                }
        }
        if ((res < 1) && (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,
@@ -1319,37 +2030,29 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc
                if (!iax2_getpeername(*sin, host, sizeof(host), lockpeer))
                        snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port));
 
-               gettimeofday(&now, NULL);
-
-               start = 2 + (rand() % (TRUNK_CALL_START - 1));
-               for (x = start; 1; x++) {
-                       if (x == TRUNK_CALL_START) {
-                               x = 1;
-                               continue;
-                       }
-
-                       /* 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;
-                       }
+               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;
                }
-               /* 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, lockpeer, host);
                update_max_nontrunk();
                if (iaxs[x]) {
                        if (option_debug && iaxdebug)
                                ast_log(LOG_DEBUG, "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;
@@ -1369,6 +2072,7 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc
                } else {
                        ast_log(LOG_WARNING, "Out of resources\n");
                        ast_mutex_unlock(&iaxsl[x]);
+                       replace_callno(callno_entry);
                        return 0;
                }
                ast_mutex_unlock(&iaxsl[x]);
@@ -2112,6 +2816,8 @@ static int iax2_show_peer(int fd, int argc, char *argv[])
                ast_cli(fd, "  Context      : %s\n", peer->context);
                ast_cli(fd, "  Mailbox      : %s\n", peer->mailbox);
                ast_cli(fd, "  Dynamic      : %s\n", ast_test_flag(peer, IAX_DYNAMIC) ? "Yes":"No");
+               ast_cli(fd, "  Callnum limit: %d\n", peer->maxcallno);
+               ast_cli(fd, "  Calltoken req: %s\n", (peer->calltoken_required == CALLTOKEN_YES) ? "Yes" : ((peer->calltoken_required == CALLTOKEN_AUTO) ? "Auto" : "No"));
                ast_cli(fd, "  Callerid     : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, "<unspecified>"));
                ast_cli(fd, "  Expire       : %d\n", peer->expire);
                ast_cli(fd, "  ACL          : %s\n", (peer->ha?"Yes":"No"));
@@ -3104,13 +3810,248 @@ struct parsed_dial_string {
 
 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 {
+               struct ast_iax2_full_hdr f;
+               struct iax_ie_data ied;
+       } data;
+       size_t size = sizeof(struct ast_iax2_full_hdr);
+
+       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, 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_ already have a destination callno
+        * 2. Frame must be a valid iax_frame subclass capable of starting dialog
+        * 3. 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.
+        * 4. Pvt's calltoken_ie_len must be _LESS_ than the total IE length
+        * 5. 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->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_mutex_lock(&iaxq.lock);
+       if (f->prev)
+               f->prev->next = f->next;
+       else
+               iaxq.head = f->next;
+       if (f->next)
+               f->next->prev = f->prev;
+       else
+               iaxq.tail = f->prev;
+       iaxq.count--;
+       ast_mutex_unlock(&iaxq.lock);
+
+       /* ---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 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 iax2_user *user = NULL;
+       struct iax2_peer *peer = NULL;
 
-       return sendto(sockfd, &f, sizeof(f), 0, (struct sockaddr *)sin, sizeof(*sin));
+       if (ast_strlen_zero(name)) {
+               return; /* no username given */
+       }
+
+       if ((subclass == IAX_COMMAND_NEW) && (user = find_user(name, 1)) && (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;
+       }
+}
+
+/*!
+ * \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) */
+       char buf[256] = { 0 };
+       time_t t = time(NULL);
+       char hash[41]; /* 40 char sha1 hash */
+       int subclass = uncompress_subclass(fh->csub);
+       char iabuf[INET_ADDRSTRLEN];
+
+       /* ----- 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 */
+               snprintf(buf, sizeof(buf), CALLTOKEN_HASH_FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), sin->sin_port, (unsigned int) t, randomcalltokendata);
+               ast_sha1_hash(hash, buf);
+
+               snprintf(buf, sizeof(buf), CALLTOKEN_IE_FORMAT, (unsigned int) t, hash);
+               iax_ie_append_str(&ied, IAX_IE_CALLTOKEN, 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 */
+               snprintf(buf, sizeof(buf), CALLTOKEN_HASH_FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), sin->sin_port, (unsigned int) rec_time, randomcalltokendata);
+               ast_sha1_hash(hash, 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(iabuf, sizeof(iabuf), 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(iabuf, sizeof(iabuf), 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(iabuf, sizeof(iabuf), 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;
 }
 
 /*!
@@ -3297,6 +4238,7 @@ static int iax2_call(struct ast_channel *c, char *dest, int timeout)
        iaxs[callno]->sockfd = cai.sockfd;
 
        /* 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]);
@@ -5719,8 +6661,11 @@ static int complete_dpreply(struct chan_iax2_pvt *pvt, struct iax_ies *ies)
                        }
                        /* Wake up waiters */
                        for (x=0;x<sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++)
-                               if (dp->waiters[x] > -1)
-                                       write(dp->waiters[x], "asdf", 4);
+                               if (dp->waiters[x] > -1) {
+                                       if (write(dp->waiters[x], "asdf", 4) == -1) {
+                                               ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
+                                       }
+                               }
                }
                prev = dp;
                dp = dp->peer;
@@ -5742,6 +6687,13 @@ static int complete_transfer(int callno, struct iax_ies *ies)
                ast_log(LOG_WARNING, "Invalid transfer request\n");
                return -1;
        }
+
+       /* 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 */
@@ -5939,6 +6891,8 @@ static int expire_registry(void *data)
        ast_log(LOG_DEBUG, "Expiring registration for peer '%s'\n", p->name);
        if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(p, IAX_TEMPONLY|IAX_RTCACHEFRIENDS)))
                realtime_update_peer(p->name, &p->addr, 0);
+       /* modify entry in peercnts table as _not_ registered */
+       peercnt_modify(0, 0, &p->addr);
        /* Reset the address */
        memset(&p->addr, 0, sizeof(p->addr));
        /* Reset expire notice */
@@ -6031,6 +6985,10 @@ static int update_registry(char *name, struct sockaddr_in *sin, int callno, char
        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(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port), p->expiry);
@@ -6054,7 +7012,13 @@ static int update_registry(char *name, struct sockaddr_in *sin, int callno, char
                /* 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);
+       }
+
        /* Store socket fd */
        p->sockfd = fd;
        /* Setup the expiry */
@@ -6182,6 +7146,7 @@ static int registry_rerequest(struct iax_ies *ies, int callno, struct sockaddr_i
                                res = authenticate(challenge, reg->secret, NULL, authmethods, &ied, sin, NULL, 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;
@@ -6879,17 +7844,49 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                /* 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, ntohs(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;
                }
+               f.datalen = res - sizeof(*fh);
+               if (f.datalen) {
+                       if (f.frametype == AST_FRAME_IAX) {
+                               if (iax_parse_ies(&ies, buf + sizeof(*fh), f.datalen)) {
+                                       ast_log(LOG_WARNING, "Undecodable frame received from '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr));
+                                       return 1;
+                               }
+                               f.data = NULL;
+                               f.datalen = 0;
+                       } else {
+                               f.data = buf + sizeof(*fh);
+                               memset(&ies, 0, sizeof(ies));
+                       }
+               } else {
+                       if (f.frametype == AST_FRAME_IAX)
+                               f.data = NULL;
+                       else
+                               f.data = 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 ((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;
+                       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;
@@ -6915,7 +7912,14 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                        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, 1, fd, check_dcallno);
+               if (!(fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, 1, 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) 
@@ -7042,12 +8046,6 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                            (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 %zd min)\n", res, sizeof(*fh));
-                       ast_mutex_unlock(&iaxsl[fr->callno]);
-                       return 1;
-               }
                /* Ensure text frames are NULL-terminated */
                if (f.frametype == AST_FRAME_TEXT && buf[res - 1] != '\0') {
                        if (res < sizeof(buf))
@@ -7055,7 +8053,6 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                        else /* Trims one character from the text message, but that's better than overwriting the end of the buffer. */
                                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 */
@@ -7111,25 +8108,6 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                        return 1;
                }
 
-               if (f.datalen) {
-                       if (f.frametype == AST_FRAME_IAX) {
-                               if (iax_parse_ies(&ies, buf + sizeof(*fh), f.datalen)) {
-                                       ast_log(LOG_WARNING, "Undecodable frame received from '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr));
-                                       ast_mutex_unlock(&iaxsl[fr->callno]);
-                                       return 1;
-                               }
-                               f.data = NULL;
-                               f.datalen = 0;
-                       } else
-                               f.data = buf + sizeof(*fh);
-               } else {
-                       if (f.frametype == AST_FRAME_IAX)
-                               f.data = NULL;
-                       else
-                               f.data = 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 */
@@ -7992,6 +8970,28 @@ retryowner2:
                                else
                                        send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_FWDATA, 0, ied0.buf, ied0.pos, -1);
                                break;
+                       case IAX_COMMAND_CALLTOKEN:
+                       {
+                               struct iax_frame *cur;
+                               int found = 0;
+
+                               ast_mutex_lock(&iaxq.lock);
+                               for (cur = iaxq.head; cur ; cur = cur->next) {
+                                       /* 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 == iaxs[fr->callno]->callno) {
+                                               found = 1;
+                                               break;
+                                       }
+                               }
+                               ast_mutex_unlock(&iaxq.lock);
+                               /* find last sent frame */
+                               if (cur && found && ies.calltoken && ies.calltokendata) {
+                                       resend_with_token(fr->callno, cur, (char *) ies.calltokendata);
+                               }
+                               break;
+                       }
                        default:
                                ast_log(LOG_DEBUG, "Unknown IAX command %d on %d/%d\n", f.subclass, fr->callno, iaxs[fr->callno]->peercallno);
                                memset(&ied0, 0, sizeof(ied0));
@@ -8144,6 +9144,7 @@ static int iax2_do_register(struct iax2_registry *reg)
        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;
@@ -8290,6 +9291,10 @@ static int iax2_poke_noanswer(void *data)
 
 static int iax2_poke_peer(struct iax2_peer *peer, int heldcall)
 {
+       struct iax_ie_data ied = {
+               .buf = { 0 },
+               .pos = 0,
+       };
        if (!peer->maxms || !peer->addr.sin_addr.s_addr) {
                /* IF we have no IP, or this isn't to be monitored, return
                  imeediately after clearing things out */
@@ -8317,8 +9322,10 @@ static int iax2_poke_peer(struct iax2_peer *peer, int heldcall)
        /* Speed up retransmission times */
        iaxs[peer->callno]->pingtime = peer->maxms / 4 + 1;
        iaxs[peer->callno]->peerpoke = peer;
-       send_command(iaxs[peer->callno], AST_FRAME_IAX, IAX_COMMAND_POKE, 0, NULL, 0, -1);
-       
+
+       add_empty_calltoken_ie(iaxs[peer->callno], &ied); /* this _MUST_ be the last ie added */
+       send_command(iaxs[peer->callno], AST_FRAME_IAX, IAX_COMMAND_POKE, 0, ied.buf, ied.pos, -1);
+
        /* If the host is already unreachable then use the unreachable interval instead */
        if (peer->lastms < 0) {
                peer->pokeexpire = ast_sched_add(sched, peer->pokefreqnotok, iax2_poke_noanswer, peer);
@@ -8677,6 +9684,9 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, in
                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;
                peer->context[0] = '\0';
                peer->peercontext[0] = '\0';
                ast_clear_flag(peer, IAX_HASCALLERID);
@@ -8803,7 +9813,24 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, in
                                } else ast_log(LOG_WARNING, "Set peer->pokefreqnotok to %d\n", peer->pokefreqnotok);
                        } else if (!strcasecmp(v->name, "timezone")) {
                                ast_copy_string(peer->zonetag, v->value, sizeof(peer->zonetag));
-                       }/* 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;
                }
@@ -8871,6 +9898,7 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, in
                user->prefs = prefs;
                user->capability = iax2_capability;
                user->encmethods = iax2_encryption;
+               user->calltoken_required = CALLTOKEN_DEFAULT;
                ast_copy_string(user->name, name, sizeof(user->name));
                ast_copy_string(user->language, language, sizeof(user->language));
                ast_copy_flags(user, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_CODEC_USER_FIRST | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP); 
@@ -8963,7 +9991,18 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, in
                                user->maxauthreq = atoi(v->value);
                                if (user->maxauthreq < 0)
                                        user->maxauthreq = 0;
-                       }/* 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;
                }
@@ -9119,7 +10158,7 @@ static void set_timing(void)
 
 
 /*--- set_config: 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;
        int capability=iax2_capability;
@@ -9157,6 +10196,8 @@ static int set_config(char *config_file, int reload)
 
        min_reg_expire = IAX_DEFAULT_REG_EXPIRE;
        max_reg_expire = IAX_DEFAULT_REG_EXPIRE;
+       global_maxcallno = DEFAULT_MAXCALLNO_LIMIT;
+       global_maxcallno_nonval = DEFAULT_MAXCALLNO_LIMIT_NONVAL;
 
        maxauthreq = 0;
 
@@ -9322,7 +10363,19 @@ static int set_config(char *config_file, int reload)
                        maxauthreq = atoi(v->value);
                        if (maxauthreq < 0)
                                maxauthreq = 0;
-               } /*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;
        }
@@ -9357,7 +10410,9 @@ static int set_config(char *config_file, int reload)
        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), 0);
                                        if (user) {
@@ -9392,7 +10447,7 @@ static int set_config(char *config_file, int reload)
 
 static int reload_config(void)
 {
-       char *config = "iax.conf";
+       static const char config[] = "iax.conf";
        struct iax2_registry *reg;
        struct iax2_peer *peer;
        ast_copy_string(accountcode, "", sizeof(accountcode));
@@ -9403,9 +10458,16 @@ static int reload_config(void)
        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);
+
        set_config(config,1);
        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);
+
        ast_mutex_lock(&reg_lock);
        for (reg = registrations; reg; reg = reg->next)
                iax2_do_register(reg);
@@ -9493,6 +10555,7 @@ static int cache_get_callno_locked(const char *data)
        if (pds.key)
                ast_copy_string(iaxs[callno]->outkey, pds.key, sizeof(iaxs[callno]->outkey));
        /* 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;
@@ -9925,6 +10988,10 @@ static char show_peer_usage[] =
 "Usage: iax show peer <name>\n"
 "       Display details on specific IAX peer\n";
 
+static char show_callnumber_usage[] =
+"Usage: iax2 show callnumber usage <ip optional>\n"
+"       Show current entries in the ip Call Number Limit table.\n";
+
 static char prune_realtime_usage[] =
 "Usage: iax2 prune realtime [<peername>|all]\n"
 "       Prunes object(s) from the cache\n";
@@ -10034,6 +11101,8 @@ static struct ast_cli_entry iax2_cli[] = {
          "Show defined IAX peers", show_peers_usage },
        { { "iax2", "show", "registry", NULL }, iax2_show_registry,
          "Show IAX registration status", show_reg_usage },
+       { { "iax2", "show", "callnumber", "usage", NULL }, iax2_show_callnumber_usage,
+         "Show current entries in ip Call number limit table", show_callnumber_usage },
        { { "iax2", "debug", NULL }, iax2_do_debug,
          "Enable IAX debugging", debug_usage },
        { { "iax2", "trunk", "debug", NULL }, iax2_do_trunk_debug,
@@ -10092,11 +11161,16 @@ static int __unload_module(void)
                ast_mutex_destroy(&iaxsl[x]);
 
        ao2_ref(iax_peercallno_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);
 
        return 0;
 }
 
-int unload_module()
+int unload_module(void)
 {
        ast_custom_function_unregister(&iaxpeer_function);
        return __unload_module();
@@ -10120,15 +11194,60 @@ static int pvt_cmp_cb(void *obj, void *arg, int flags)
                pvt2->frames_received) ? CMP_MATCH : 0;
 }
 
+static int load_objects(void)
+{
+       iax_peercallno_pvts = peercnts = callno_limits = calltoken_ignores = callno_pool = callno_pool_trunk = NULL;
+       if (!(iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, 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 (iax_peercallno_pvts) {
+               ao2_ref(iax_peercallno_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 -1;
+}
+
 /*--- load_module: Load IAX2 module, load configuraiton ---*/
 int load_module(void)
 {
-       char *config = "iax.conf";
+       static const char config[] = "iax.conf";
        int res = 0;
        int x;
        struct iax2_registry *reg;
        struct iax2_peer *peer;
-       
+
+       if (load_objects()) {
+               return -1;
+       }
+
+       randomcalltokendata = rand();
+
        ast_custom_function_register(&iaxpeer_function);
 
        iax_set_output(iax_debug_output);
@@ -10207,11 +11326,6 @@ int load_module(void)
                ast_netsock_release(outsock);
        }
 
-       iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb);
-       if (!iax_peercallno_pvts) {
-               res = -1;
-       }
-
        ast_mutex_lock(&reg_lock);
        for (reg = registrations; reg; reg = reg->next)
                iax2_do_register(reg);
@@ -10228,17 +11342,17 @@ int load_module(void)
        return res;
 }
 
-char *description()
+char *description(void)
 {
        return (char *) desc;
 }
 
-int usecount()
+int usecount(void)
 {
        return usecnt;
 }
 
-char *key()
+char *key(void)
 {
        return ASTERISK_GPL_KEY;
 }
index 6db037455b967771c7d3f52331e177a4c3b327f7..3b7ea54bd3a232af4ae248257f8a447870e69efe 100644 (file)
@@ -433,7 +433,10 @@ void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, s
                "TRANSFR",
                "PROVISN",
                "FWDWNLD",
-               "FWDATA "
+               "FWDATA ",
+               "TXMEDIA",
+               "RTKEY  ",
+               "CTOKEN ",
        };
        const char *cmds[] = {
                "(0?)",
@@ -873,6 +876,12 @@ int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen)
                                ies->rr_ooo = ntohl(get_unaligned_uint32(data + 2));
                        }
                        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 50e04538e2ac42c7bd13221443783cb22667cbfe..e69e7e3688f25504c40c2e74d186cc833a13cb84 100644 (file)
@@ -71,6 +71,8 @@ struct iax_ies {
        unsigned short rr_delay;
        unsigned int rr_dropped;
        unsigned int rr_ooo;
+       unsigned char calltoken;
+       unsigned char *calltokendata;
 };
 
 #define DIRECTION_INGRESS 1
index c6b44628de3a5e93df8f0e05593366c2dd5ddb3e..05bb799e19931a91586e5e4f32629413825307b9 100644 (file)
@@ -67,6 +67,7 @@
 #define IAX_COMMAND_PROVISION  35      /* Provision device */
 #define IAX_COMMAND_FWDOWNL    36      /* Download firmware */
 #define IAX_COMMAND_FWDATA     37      /* Firmware Data */
+#define IAX_COMMAND_CALLTOKEN 40  /*! Call number token */
 
 #define IAX_DEFAULT_REG_EXPIRE  60     /* By default require re-registration once per minute */
 
 #define IAX_IE_RR_DELAY                                49              /* Max playout delay for received frames (in ms) u16 */
 #define IAX_IE_RR_DROPPED                      50              /* Dropped frames (presumably by jitterbuf) u32 */
 #define IAX_IE_RR_OOO                          51              /* Frames received Out of Order u32 */
+#define IAX_IE_CALLTOKEN                       54              /* Call number security token */
 
 
 #define IAX_AUTH_PLAINTEXT                     (1 << 0)
index bcbb58c506d44825008ca02a98de64fa8dc02f33..70e62e70e66308a2c2775091b9f599c511c94110 100644 (file)
@@ -286,6 +286,62 @@ autokill=yes
                                ; has expired based on its registration interval, used the stored
                                ; address information regardless. (yes|no)
 
+;
+; 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.
 ;
index ad946d57eb68407e2a027683a8a2077c12bbab25..0bc79b89773ec99632bd70f12ca648adb43d6e87 100644 (file)
@@ -36,10 +36,17 @@ extern "C" {
 
 /* Host based access control */
 
-struct ast_ha;
-
-extern void ast_free_ha(struct ast_ha *ha);
-extern struct ast_ha *ast_append_ha(char *sense, char *stuff, struct ast_ha *path);
+struct ast_ha {
+       /* Host access rule */
+       struct in_addr netaddr;
+       struct in_addr netmask;
+       int sense;
+       struct ast_ha *next;
+};
+
+void ast_free_ha(struct ast_ha *ha);
+void ast_copy_ha(const struct ast_ha *from, struct ast_ha *to);
+struct ast_ha *ast_append_ha(char *sense, const char *stuff, struct ast_ha *path);
 extern int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin);
 extern int ast_get_ip(struct sockaddr_in *sin, const char *value);
 extern int ast_get_ip_or_srv(struct sockaddr_in *sin, const char *value, const char *service);
index 8d83a8d23320d71af1efecea905b1794bbe44954..96437bfb83bbf5a87db7b29317610bd5b069dbc1 100644 (file)
@@ -273,6 +273,16 @@ enum search_flags {
         *  This implies that it can be passed to the object's hash function
         *  for optimized searching. */
        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),
+
 };
 
 /*!
diff --git a/include/asterisk/sha1.h b/include/asterisk/sha1.h
new file mode 100644 (file)
index 0000000..fa8e215
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *  sha1.h
+ *
+ *  Description:
+ *      This is the header file for code which implements the Secure
+ *      Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
+ *      April 17, 1995.
+ *
+ *      Many of the variable names in this code, especially the
+ *      single character names, were used because those were the names
+ *      used in the publication.
+ *
+ *      Please read the file sha1.c for more information.
+ *
+ */
+
+
+#ifndef _SHA1_H_
+#define _SHA1_H_
+
+
+
+#if defined(__OpenBSD__) || defined( __FreeBSD__)
+#include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
+
+/*
+ * If you do not have the ISO standard stdint.h header file, then you
+ * must typdef the following:
+ *    name              meaning
+ *  uint32_t         unsigned 32 bit integer
+ *  uint8_t          unsigned 8 bit integer (i.e., unsigned char)
+ *
+ */
+
+#ifndef _SHA_enum_
+#define _SHA_enum_
+enum
+{
+    shaSuccess = 0,
+    shaNull,            /* Null pointer parameter */
+    shaInputTooLong,    /* input data too long */
+    shaStateError       /* called Input after Result */
+};
+#endif
+#define SHA1HashSize 20
+
+/*
+ *  This structure will hold context information for the SHA-1
+ *  hashing operation
+ */
+typedef struct SHA1Context
+{
+    uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest  */
+
+    uint32_t Length_Low;            /* Message length in bits      */
+    uint32_t Length_High;           /* Message length in bits      */
+
+                               /* Index into message block array   */
+    uint32_t Message_Block_Index;      /* 8 bits actually suffice */
+    uint8_t Message_Block[64];      /* 512-bit message blocks      */
+
+    int Computed;               /* Is the digest computed?         */
+    int Corrupted;             /* Is the message digest corrupted? */
+} SHA1Context;
+
+/*
+ *  Function Prototypes
+ */
+
+
+int SHA1Reset(  SHA1Context *);
+int SHA1Input(  SHA1Context *,
+                const uint8_t *,
+                unsigned int);
+int SHA1Result( SHA1Context *,
+                uint8_t Message_Digest[SHA1HashSize]);
+
+#endif
index 0f1df400319f5b0d81cd2b2c44074b7c4d7c296f..dce96f405dcca63bcb9c918f8cdf9ed0474e7726 100644 (file)
@@ -144,6 +144,10 @@ struct hostent *ast_gethostbyname(const char *host, struct ast_hostent *hp);
        \brief Produces MD5 hash based on input string */
 void ast_md5_hash(char *output, char *input);
 
+/* ast_sha1_hash
+       \brief Produces SHA1 hash based on input string */
+void ast_sha1_hash(char *output, char *input);
+
 int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max);
 int ast_base64decode(unsigned char *dst, const char *src, int max);
 
diff --git a/sha1.c b/sha1.c
new file mode 100644 (file)
index 0000000..16ddd6a
--- /dev/null
+++ b/sha1.c
@@ -0,0 +1,385 @@
+/*
+ *
+ * Based on the RFC 3174
+ * 
+ * Full Copyright Statement
+ *
+ *  Copyright (C) The Internet Society (2001).  All Rights Reserved.
+ *
+ *  This document and translations of it may be copied and furnished to
+ *  others, and derivative works that comment on or otherwise explain it
+ *  or assist in its implementation may be prepared, copied, published
+ *  and distributed, in whole or in part, without restriction of any
+ *  kind, provided that the above copyright notice and this paragraph are
+ *  included on all such copies and derivative works.  However, this
+ *  document itself may not be modified in any way, such as by removing
+ *  the copyright notice or references to the Internet Society or other
+ *  Internet organizations, except as needed for the purpose of
+ *  developing Internet standards in which case the procedures for
+ *  copyrights defined in the Internet Standards process must be
+ *  followed, or as required to translate it into languages other than
+ *  English.
+ *
+ *  The limited permissions granted above are perpetual and will not be
+ *  revoked by the Internet Society or its successors or assigns.
+
+ *  This document and the information contained herein is provided on an
+ *  "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ *  TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ *  BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ *  HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *
+ * 
+ *  Description:
+ *       This file implements the Secure Hashing Algorithm 1 as
+ *       defined in FIPS PUB 180-1 published April 17, 1995.
+ *
+ *       The SHA-1, produces a 160-bit message digest for a given
+ *       data stream.  It should take about 2**n steps to find a
+ *       message with the same digest as a given message and
+ *       2**(n/2) to find any two messages with the same digest,
+ *       when n is the digest size in bits.  Therefore, this
+ *       algorithm can serve as a means of providing a
+ *       "fingerprint" for a message.
+ *
+ *  Portability Issues:
+ *       SHA-1 is defined in terms of 32-bit "words".  This code
+ *       uses <stdint.h> (included via "sha1.h" to define 32 and 8
+ *       bit unsigned integer types.  If your C compiler does not
+ *       support 32 bit unsigned integers, this code is not
+ *       appropriate.
+ *
+ *  Caveats:
+ *       SHA-1 is designed to work with messages less than 2^64 bits
+ *       long.  Although SHA-1 allows a message digest to be generated
+ *       for messages of any number of bits less than 2^64, this
+ *       implementation only works with messages with a length that is
+ *       a multiple of the size of an 8-bit character.
+ *
+ */
+
+
+#include "asterisk/sha1.h"
+
+/*
+ *  Define the SHA1 circular left shift macro
+ */
+#define SHA1CircularShift(bits,word) \
+                        (((word) << (bits)) | ((word) >> (32-(bits))))
+
+/* Local Function Prototyptes */
+void SHA1PadMessage(SHA1Context *);
+void SHA1ProcessMessageBlock(SHA1Context *);
+
+/*
+ *  SHA1Reset
+ *
+ *  Description:
+ *       This function will initialize the SHA1Context in preparation
+ *       for computing a new SHA1 message digest.
+ *
+ *  Parameters:
+ *       context: [in/out]
+ *                The context to reset.
+ *
+ *  Returns:
+ *       sha Error Code.
+ *
+ */
+int SHA1Reset(SHA1Context *context)
+{
+       if (!context) {
+               return shaNull;
+       }
+
+       context->Length_Low             = 0;
+       context->Length_High            = 0;
+       context->Message_Block_Index    = 0;
+
+       context->Intermediate_Hash[0]   = 0x67452301;
+       context->Intermediate_Hash[1]   = 0xEFCDAB89;
+       context->Intermediate_Hash[2]   = 0x98BADCFE;
+       context->Intermediate_Hash[3]   = 0x10325476;
+       context->Intermediate_Hash[4]   = 0xC3D2E1F0;
+
+       context->Computed               = 0;
+       context->Corrupted              = 0;
+
+       return shaSuccess;
+}
+
+/*
+ *  SHA1Result
+ *
+ *  Description:
+ *       This function will return the 160-bit message digest into the
+ *       Message_Digest array  provided by the caller.
+ *       NOTE: The first octet of hash is stored in the 0th element,
+ *                  the last octet of hash in the 19th element.
+ *
+ *  Parameters:
+ *       context: [in/out]
+ *                The context to use to calculate the SHA-1 hash.
+ *       Message_Digest: [out]
+ *                Where the digest is returned.
+ *
+ *  Returns:
+ *       sha Error Code.
+ *
+ */
+int SHA1Result( SHA1Context *context,
+                        uint8_t Message_Digest[SHA1HashSize])
+{
+       int i;
+
+       if (!context || !Message_Digest) {
+               return shaNull;
+       }
+
+       if (context->Corrupted) {
+               return context->Corrupted;
+       }
+
+       if (!context->Computed) {
+               SHA1PadMessage(context);
+               for (i = 0; i < 64; ++i) {
+                       /* message may be sensitive, clear it out */
+                       context->Message_Block[i] = 0;
+               }
+               context->Length_Low = 0;    /* and clear length */
+               context->Length_High = 0;
+               context->Computed = 1;
+       }
+
+       for (i = 0; i < SHA1HashSize; ++i) {
+               Message_Digest[i] = context->Intermediate_Hash[i >> 2] >> 8 * ( 3 - ( i & 0x03 ) );
+       }
+
+       return shaSuccess;
+}
+
+/*
+ *  SHA1Input
+ *
+ *  Description:
+ *       This function accepts an array of octets as the next portion
+ *       of the message.
+ *
+ *  Parameters:
+ *       context: [in/out]
+ *                The SHA context to update
+ *       message_array: [in]
+ *                An array of characters representing the next portion of
+ *                the message.
+ *       length: [in]
+ *                The length of the message in message_array
+ *
+ *  Returns:
+ *       sha Error Code.
+ *
+ */
+int SHA1Input(SHA1Context *context, const uint8_t *message_array, unsigned length)
+{
+       if (!length) {
+               return shaSuccess;
+       }
+
+       if (!context || !message_array) {
+               return shaNull;
+       }
+
+       if (context->Computed) {
+               context->Corrupted = shaStateError;
+               return shaStateError;
+       }
+
+       if (context->Corrupted) {
+               return context->Corrupted;
+       }
+
+       while (length-- && !context->Corrupted) {
+               context->Message_Block[context->Message_Block_Index++] = (*message_array & 0xFF);
+
+               context->Length_Low += 8;
+               if (context->Length_Low == 0) {
+                       context->Length_High++;
+                       if (context->Length_High == 0) {
+                               /* Message is too long */
+                               context->Corrupted = 1;
+                       }
+               }
+
+               if (context->Message_Block_Index == 64) {
+                       SHA1ProcessMessageBlock(context);
+               }
+
+               message_array++;
+       }
+
+       return shaSuccess;
+}
+
+/*
+ *  SHA1ProcessMessageBlock
+ *
+ *  Description:
+ *       This function will process the next 512 bits of the message
+ *       stored in the Message_Block array.
+ *
+ *  Parameters:
+ *       None.
+ *
+ *  Returns:
+ *       Nothing.
+ *
+ *  Comments:
+ *       Many of the variable names in this code, especially the
+ *       single character names, were used because those were the
+ *       names used in the publication.
+ *
+ *
+ */
+void SHA1ProcessMessageBlock(SHA1Context *context)
+{
+       const uint32_t K[] =     {     /* Constants defined in SHA-1  */
+                                0x5A827999,
+                                0x6ED9EBA1,
+                                0x8F1BBCDC,
+                                0xCA62C1D6
+                                };
+       int             t;                 /* Loop counter                */
+       uint32_t    temp;              /* Temporary word value        */
+       uint32_t    W[80];             /* Word sequence               */
+       uint32_t    A, B, C, D, E;     /* Word buffers                */
+
+       /*
+        *  Initialize the first 16 words in the array W
+        */
+       for (t = 0; t < 16; t++) {
+               W[t] = context->Message_Block[t * 4] << 24;
+               W[t] |= context->Message_Block[t * 4 + 1] << 16;
+               W[t] |= context->Message_Block[t * 4 + 2] << 8;
+               W[t] |= context->Message_Block[t * 4 + 3];
+       }
+
+       for (t = 16; t < 80; t++) {
+               W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+       }
+
+       A = context->Intermediate_Hash[0];
+       B = context->Intermediate_Hash[1];
+       C = context->Intermediate_Hash[2];
+       D = context->Intermediate_Hash[3];
+       E = context->Intermediate_Hash[4];
+
+       for (t = 0; t < 20; t++) {
+               temp = SHA1CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
+               E = D;
+               D = C;
+               C = SHA1CircularShift(30,B);
+               B = A;
+               A = temp;
+       }
+
+       for (t = 20; t < 40; t++) {
+               temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
+               E = D;
+               D = C;
+               C = SHA1CircularShift(30,B);
+               B = A;
+               A = temp;
+       }
+
+       for (t = 40; t < 60; t++) {
+               temp = SHA1CircularShift(5,A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
+               E = D;
+               D = C;
+               C = SHA1CircularShift(30,B);
+               B = A;
+               A = temp;
+       }
+
+       for (t = 60; t < 80; t++) {
+               temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
+               E = D;
+               D = C;
+               C = SHA1CircularShift(30,B);
+               B = A;
+               A = temp;
+       }
+
+       context->Intermediate_Hash[0] += A;
+       context->Intermediate_Hash[1] += B;
+       context->Intermediate_Hash[2] += C;
+       context->Intermediate_Hash[3] += D;
+       context->Intermediate_Hash[4] += E;
+
+       context->Message_Block_Index = 0;
+}
+
+
+/*
+ *  SHA1PadMessage
+ *
+ *  Description:
+ *       According to the standard, the message must be padded to an even
+ *       512 bits.  The first padding bit must be a '1'.  The last 64
+ *       bits represent the length of the original message.  All bits in
+ *       between should be 0.  This function will pad the message
+ *       according to those rules by filling the Message_Block array
+ *       accordingly.  It will also call the ProcessMessageBlock function
+ *       provided appropriately.  When it returns, it can be assumed that
+ *       the message digest has been computed.
+ *
+ *  Parameters:
+ *       context: [in/out]
+ *                The context to pad
+ *       ProcessMessageBlock: [in]
+ *                The appropriate SHA*ProcessMessageBlock function
+ *  Returns:
+ *       Nothing.
+ *
+ */
+
+void SHA1PadMessage(SHA1Context *context)
+{
+       /*
+        *  Check to see if the current message block is too small to hold
+        *  the initial padding bits and length.  If so, we will pad the
+        *  block, process it, and then continue padding into a second
+        *  block.
+        */
+       if (context->Message_Block_Index > 55) {
+               context->Message_Block[context->Message_Block_Index++] = 0x80;
+               while (context->Message_Block_Index < 64) {
+                       context->Message_Block[context->Message_Block_Index++] = 0;
+               }
+
+               SHA1ProcessMessageBlock(context);
+
+               while (context->Message_Block_Index < 56) {
+                       context->Message_Block[context->Message_Block_Index++] = 0;
+               }
+       } else {
+               context->Message_Block[context->Message_Block_Index++] = 0x80;
+               while (context->Message_Block_Index < 56) {
+                       context->Message_Block[context->Message_Block_Index++] = 0;
+               }
+       }
+
+       /*
+        *  Store the message length as the last 8 octets
+        */
+       context->Message_Block[56] = context->Length_High >> 24;
+       context->Message_Block[57] = context->Length_High >> 16;
+       context->Message_Block[58] = context->Length_High >> 8;
+       context->Message_Block[59] = context->Length_High;
+       context->Message_Block[60] = context->Length_Low >> 24;
+       context->Message_Block[61] = context->Length_Low >> 16;
+       context->Message_Block[62] = context->Length_Low >> 8;
+       context->Message_Block[63] = context->Length_Low;
+
+       SHA1ProcessMessageBlock(context);
+}
diff --git a/utils.c b/utils.c
index 7dc020c830bf7aaa9806d003eb1e352b95e99a39..a284a937d02e2a516d877f6601467e0d58f0be4a 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/io.h"
 #include "asterisk/logger.h"
 #include "asterisk/md5.h"
+#include "asterisk/sha1.h"
 #include "asterisk/options.h"
 #include "asterisk/compat.h"
 
@@ -297,6 +298,24 @@ void ast_md5_hash(char *output, char *input)
                ptr += sprintf(ptr, "%2.2x", digest[x]);
 }
 
+/*! \brief Produce 40 char SHA1 hash of value. */
+void ast_sha1_hash(char *output, char *input)
+{
+       struct SHA1Context sha;
+       char *ptr;
+       int x;
+       uint8_t Message_Digest[20];
+
+       SHA1Reset(&sha);
+       
+       SHA1Input(&sha, (const unsigned char *) input, strlen(input));
+
+       SHA1Result(&sha, Message_Digest);
+       ptr = output;
+       for (x = 0; x < 20; x++)
+               ptr += sprintf(ptr, "%2.2x", Message_Digest[x]);
+}
+
 int ast_base64decode(unsigned char *dst, const char *src, int max)
 {
        int cnt = 0;