aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordvossel <dvossel@f38db490-d61c-443f-a65b-d21fe96a405b>2009-09-03 19:10:21 +0000
committerdvossel <dvossel@f38db490-d61c-443f-a65b-d21fe96a405b>2009-09-03 19:10:21 +0000
commitfbbafcf2bfae9f0446633803b97079a967b6d109 (patch)
treed7e4e9a241633175663de32b5a0d40be82f69258
parente299521659bf793bc7286dc71215ac86abd720a1 (diff)
Merge code associated with AST-2009-006
(closes issue #12912) Reported by: rathaus Tested by: tilghman, russell, dvossel, dbrooks git-svn-id: http://svn.digium.com/svn/asterisk/tags/1.4.26.2@216015 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--channels/chan_iax2.c1338
-rw-r--r--channels/iax2-parser.c10
-rw-r--r--channels/iax2-parser.h2
-rw-r--r--channels/iax2.h3
-rw-r--r--configs/iax.conf.sample56
-rw-r--r--include/asterisk/acl.h12
-rw-r--r--include/asterisk/astobj2.h10
-rw-r--r--main/acl.c12
-rw-r--r--main/astobj2.c19
9 files changed, 1323 insertions, 139 deletions
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index b06ec709a..75eeda84d 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -276,6 +276,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 {
AST_DECLARE_STRING_FIELDS(
@@ -303,6 +317,7 @@ struct iax2_user {
struct ast_ha *ha;
struct iax2_context *contexts;
struct ast_variable *vars;
+ enum calltoken_peer_enum calltoken_required; /*!< Is calltoken validation required or not, can be YES, NO, or AUTO */
};
struct iax2_peer {
@@ -353,8 +368,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 */
};
#define IAX2_TRUNK_PREFACE (sizeof(struct iax_frame) + sizeof(struct ast_iax2_meta_hdr) + sizeof(struct ast_iax2_meta_trunk_hdr))
@@ -502,6 +519,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
@@ -634,13 +653,27 @@ 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 {
AST_LIST_HEAD(, iax_frame) queue;
int count;
} iaxq;
+static int randomcalltokendata;
+
+static const time_t MAX_CALLTOKEN_DELAY = 10;
+
/*!
* This module will get much higher performance when doing a lot of
* user and peer lookups if the number of buckets is increased from 1.
@@ -660,6 +693,58 @@ static struct ao2_container *peers;
#define MAX_USER_BUCKETS MAX_PEER_BUCKETS
static struct ao2_container *users;
+
+/*! Table containing peercnt objects for every ip address consuming a callno */
+static struct ao2_container *peercnts;
+
+/*! Table containing custom callno limit rules for a range of ip addresses. */
+static struct ao2_container *callno_limits;
+
+/*! Table containing ip addresses not requiring calltoken validation */
+static struct ao2_container *calltoken_ignores;
+
+static uint16_t DEFAULT_MAXCALLNO_LIMIT = 2048;
+
+static uint16_t DEFAULT_MAXCALLNO_LIMIT_NONVAL = 8192;
+
+static uint16_t global_maxcallno;
+
+/*! Total num of call numbers allowed to be allocated without calltoken validation */
+static uint16_t global_maxcallno_nonval;
+
+static uint16_t total_nonval_callno_used = 0;
+
+/*! peer connection private, keeps track of all the call numbers
+ * consumed by a single ip address */
+struct peercnt {
+ /*! ip address consuming call numbers */
+ unsigned long addr;
+ /*! Number of call numbers currently used by this ip address */
+ uint16_t cur;
+ /*! Max call numbers allowed for this ip address */
+ uint16_t limit;
+ /*! Specifies whether limit is set by a registration or not, if so normal
+ * limit setting rules do not apply to this address. */
+ unsigned char reg;
+};
+
+/*! used by both callno_limits and calltoken_ignores containers */
+struct addr_range {
+ /*! ip address range for custom callno limit rule */
+ struct ast_ha ha;
+ /*! callno limit for this ip address range, only used in callno_limits container */
+ uint16_t limit;
+ /*! delete me marker for reloads */
+ unsigned char delme;
+};
+
+struct callno_entry {
+ /*! callno used for this entry */
+ uint16_t callno;
+ /*! was this callno calltoken validated or not */
+ unsigned char validated;
+};
+
static struct ast_firmware_list {
struct iax_firmware *wares;
ast_mutex_t lock;
@@ -819,7 +904,6 @@ static void __attribute__((format(printf, 1, 2))) jb_debug_output(const char *fm
/* 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[ARRAY_LEN(iaxs)];
-static struct timeval lastused[ARRAY_LEN(iaxs)];
/*!
* \brief Another container of iax2_pvt structures
@@ -882,6 +966,9 @@ static int decode_frame(aes_decrypt_ctx *dcx, struct ast_iax2_full_hdr *fh, stru
static int encrypt_frame(aes_encrypt_ctx *ecx, struct ast_iax2_full_hdr *fh, unsigned char *poo, int *datalen);
static void build_ecx_key(const unsigned char *digest, struct chan_iax2_pvt *pvt);
static void build_rand_pad(unsigned char *buf, ssize_t len);
+static struct callno_entry *get_unused_callno(int trunk, int validated);
+static int replace_callno(const void *obj);
+static void sched_delay_remove(struct sockaddr_in *sin, struct callno_entry *callno_entry);
static const struct ast_channel_tech iax2_tech = {
.type = "IAX2",
@@ -1346,8 +1433,7 @@ static void iax2_destroy(int callno)
retry:
pvt = iaxs[callno];
- gettimeofday(&lastused[callno], NULL);
-
+
owner = pvt ? pvt->owner : NULL;
if (owner) {
@@ -1427,6 +1513,8 @@ static void pvt_destructor(void *obj)
struct iax_frame *cur = NULL;
iax2_destroy_helper(pvt);
+ sched_delay_remove(&pvt->addr, pvt->callno_entry);
+ pvt->callno_entry = NULL;
/* Already gone */
ast_set_flag(pvt, IAX_ALREADYGONE);
@@ -1515,9 +1603,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)
{
@@ -1557,7 +1655,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;
@@ -1566,35 +1664,43 @@ 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 < ARRAY_LEN(iaxs) - 1; x++) {
- ast_mutex_lock(&iaxsl[x]);
- if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) {
- /* Update the two timers that should have been started */
- /*!
- * \note We delete these before switching the slot, because if
- * they fire in the meantime, they will generate a warning.
- */
- AST_SCHED_DEL(sched, iaxs[callno]->pingid);
- AST_SCHED_DEL(sched, iaxs[callno]->lagid);
- iaxs[x] = iaxs[callno];
- iaxs[x]->callno = x;
- iaxs[callno] = NULL;
- iaxs[x]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x);
- iaxs[x]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x);
- if (locked)
- ast_mutex_unlock(&iaxsl[callno]);
- res = x;
- if (!locked)
- ast_mutex_unlock(&iaxsl[x]);
- break;
- }
- ast_mutex_unlock(&iaxsl[x]);
- }
- if (x >= ARRAY_LEN(iaxs) - 1) {
+ if (!(callno_entry = get_unused_callno(1, iaxs[callno]->callno_entry->validated))) {
ast_log(LOG_WARNING, "Unable to trunk call: Insufficient space\n");
return -1;
}
+
+ x = callno_entry->callno;
+ ast_mutex_lock(&iaxsl[x]);
+
+ /*!
+ * \note We delete these before switching the slot, because if
+ * they fire in the meantime, they will generate a warning.
+ */
+ AST_SCHED_DEL(sched, iaxs[callno]->pingid);
+ AST_SCHED_DEL(sched, iaxs[callno]->lagid);
+ iaxs[x] = iaxs[callno];
+ iaxs[x]->callno = x;
+
+ /* since we copied over the pvt from a different callno, make sure the old entry is replaced
+ * before assigning the new one */
+ if (iaxs[x]->callno_entry) {
+ iax2_sched_add(sched, MIN_REUSE_TIME * 1000, replace_callno, iaxs[x]->callno_entry);
+ }
+ iaxs[x]->callno_entry = callno_entry;
+
+ iaxs[callno] = NULL;
+ /* Update the two timers that should have been started */
+ iaxs[x]->pingid = iax2_sched_add(sched,
+ ping_time * 1000, send_ping, (void *)(long)x);
+ iaxs[x]->lagid = iax2_sched_add(sched,
+ lagrq_time * 1000, send_lagrq, (void *)(long)x);
+
+ if (locked)
+ ast_mutex_unlock(&iaxsl[callno]);
+ res = x;
+ if (!locked)
+ ast_mutex_unlock(&iaxsl[x]);
+
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 */
@@ -1603,6 +1709,590 @@ static int make_trunk(unsigned short callno, int locked)
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 = S_OR(name, "guest");
+ int res = 1; /* required by default */
+ int optional = 0;
+ enum calltoken_peer_enum calltoken_required = CALLTOKEN_DEFAULT;
+ /* There are only two cases in which calltoken validation is not required.
+ * Case 1. sin falls within the list of address ranges specified in the calltoken optional table and
+ * the peer definition has not set the requirecalltoken option.
+ * Case 2. Username is a valid peer/user, and that peer has requirecalltoken set either auto or no.
+ */
+
+ /* ----- Case 1 ----- */
+ if ((addr_range = ao2_callback(calltoken_ignores, 0, addr_range_match_address_cb, sin))) {
+ ao2_ref(addr_range, -1);
+ optional = 1;
+ }
+
+ /* ----- Case 2 ----- */
+ if ((subclass == IAX_COMMAND_NEW) && (user = find_user(find))) {
+ calltoken_required = user->calltoken_required;
+ } else if ((subclass != IAX_COMMAND_NEW) && (peer = find_peer(find, 1))) {
+ calltoken_required = peer->calltoken_required;
+ }
+
+ if (peer) {
+ peer_unref(peer);
+ }
+ if (user) {
+ user_unref(user);
+ }
+ 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(sin->sin_addr), name, optional, calltoken_required);
+ }
+ if (((calltoken_required == CALLTOKEN_NO) || (calltoken_required == CALLTOKEN_AUTO)) ||
+ (optional && (calltoken_required == CALLTOKEN_DEFAULT))) {
+ res = 0;
+ }
+
+ return res;
+}
+
+/*!
+ * \internal
+ *
+ * \brief set peercnt callno limit.
+ *
+ * \details
+ * First looks in custom definitions. If not found, global limit
+ * is used. Entries marked as reg already have
+ * a custom limit set by a registration and are not modified.
+ */
+static void set_peercnt_limit(struct peercnt *peercnt)
+{
+ uint16_t limit = global_maxcallno;
+ struct addr_range *addr_range;
+ struct sockaddr_in sin = {
+ .sin_addr.s_addr = peercnt->addr,
+ };
+
+
+ if (peercnt->reg && peercnt->limit) {
+ return; /* this peercnt has a custom limit set by a registration */
+ }
+
+ if ((addr_range = ao2_callback(callno_limits, 0, addr_range_match_address_cb, &sin))) {
+ limit = addr_range->limit;
+ if (option_debug) {
+ ast_log(LOG_NOTICE, "custom addr_range %d found for %s\n", limit, ast_inet_ntoa(sin.sin_addr));
+ }
+ ao2_ref(addr_range, -1);
+ }
+
+ peercnt->limit = limit;
+}
+
+/*!
+ * \internal
+ * \brief sets limits for all peercnts in table. done on reload to reflect changes in conf.
+ */
+static int set_peercnt_limit_all_cb(void *obj, void *arg, int flags)
+{
+ struct peercnt *peercnt = obj;
+
+ set_peercnt_limit(peercnt);
+ 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,
+ };
+
+ 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(sin->sin_addr), peercnt->limit, peercnt->reg);
+ }
+ ao2_ref(peercnt, -1); /* decrement ref from find */
+ }
+}
+
+/*!
+ * \internal
+ * \brief adds an ip to the peercnts table, increments connection count if it already exists
+ *
+ * \details First searches for the address in the peercnts table. If found
+ * the current count is incremented. If not found a new peercnt is allocated
+ * and linked into the peercnts table with a call number count of 1.
+ */
+static int peercnt_add(struct sockaddr_in *sin)
+{
+ struct peercnt *peercnt;
+ unsigned long addr = sin->sin_addr.s_addr;
+ int res = 0;
+ struct peercnt tmp = {
+ .addr = addr,
+ };
+
+ /* Reasoning for peercnts container lock: Two identical ip addresses
+ * could be added by different threads at the "same time". Without the container
+ * lock, both threads could alloc space for the same object and attempt
+ * to link to table. With the lock, one would create the object and link
+ * to table while the other would find the already created peercnt object
+ * rather than creating a new one. */
+ ao2_lock(peercnts);
+ if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
+ ao2_lock(peercnt);
+ } else if ((peercnt = ao2_alloc(sizeof(*peercnt), NULL))) {
+ ao2_lock(peercnt);
+ /* create and set defaults */
+ peercnt->addr = addr;
+ set_peercnt_limit(peercnt);
+ /* guarantees it does not go away after unlocking table
+ * ao2_find automatically adds this */
+ ao2_link(peercnts, peercnt);
+ } else {
+ ao2_unlock(peercnts);
+ return -1;
+ }
+
+ /* check to see if the address has hit its callno limit. If not increment cur. */
+ if (peercnt->limit > peercnt->cur) {
+ peercnt->cur++;
+ if (option_debug) {
+ ast_log(LOG_NOTICE, "ip callno count incremented to %d for %s\n", peercnt->cur, ast_inet_ntoa(sin->sin_addr));
+ }
+ } else { /* max num call numbers for this peer has been reached! */
+ ast_log(LOG_ERROR, "maxcallnumber limit of %d for %s has been reached!\n", peercnt->limit, ast_inet_ntoa(sin->sin_addr));
+ res = -1;
+ }
+
+ /* clean up locks and ref count */
+ ao2_unlock(peercnt);
+ ao2_unlock(peercnts);
+ ao2_ref(peercnt, -1); /* decrement ref from find/alloc, only the container ref remains. */
+
+ return res;
+}
+
+/*!
+ * \internal
+ * \brief decrements a peercnts table entry
+ */
+static void peercnt_remove(struct peercnt *peercnt)
+{
+ struct sockaddr_in sin = {
+ .sin_addr.s_addr = peercnt->addr,
+ };
+
+ if (peercnt) {
+ /* Container locked here since peercnt may be unlinked from list. If left unlocked,
+ * peercnt_add could try and grab this entry from the table and modify it at the
+ * "same time" this thread attemps to unlink it.*/
+ ao2_lock(peercnts);
+ peercnt->cur--;
+ if (option_debug) {
+ ast_log(LOG_NOTICE, "ip callno count decremented to %d for %s\n", peercnt->cur, ast_inet_ntoa(sin.sin_addr));
+ }
+ /* if this was the last connection from the peer remove it from table */
+ if (peercnt->cur == 0) {
+ ao2_unlink(peercnts, peercnt);/* decrements ref from table, last ref is left to scheduler */
+ }
+ ao2_unlock(peercnts);
+ }
+}
+
+/*!
+ * \internal
+ * \brief called by scheduler to decrement object
+ */
+static int peercnt_remove_cb(const void *obj)
+{
+ struct peercnt *peercnt = (struct peercnt *) obj;
+
+ peercnt_remove(peercnt);
+ ao2_ref(peercnt, -1); /* decrement ref from scheduler */
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief decrements peercnts connection count, finds by addr
+ */
+static int peercnt_remove_by_addr(struct sockaddr_in *sin)
+{
+ struct peercnt *peercnt;
+ struct peercnt tmp = {
+ .addr = sin->sin_addr.s_addr,
+ };
+
+ if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
+ peercnt_remove(peercnt);
+ ao2_ref(peercnt, -1); /* decrement ref from find */
+ }
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Create callno_limit entry based on configuration
+ */
+static void build_callno_limits(struct ast_variable *v)
+{
+ struct addr_range *addr_range = NULL;
+ struct addr_range tmp;
+ struct ast_ha *ha;
+ int limit;
+ int 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;
+
+ 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(sin.sin_addr)))) {
+ ast_cli(fd, "%-15s %-12d %-12d\n", ast_inet_ntoa(sin.sin_addr), peercnt->cur, peercnt->limit);
+ found = 1;
+ break;
+ } else {
+ ast_cli(fd, "%-15s %-12d %-12d\n", ast_inet_ntoa(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(const void *obj)
+{
+ struct callno_entry *callno_entry = (struct callno_entry *) obj;
+
+ /* the callno_pool container is locked here primarily to ensure thread
+ * safety of the total_nonval_callno_used check and decrement */
+ ao2_lock(callno_pool);
+
+ if (!callno_entry->validated && (total_nonval_callno_used != 0)) {
+ total_nonval_callno_used--;
+ } else if (!callno_entry->validated && (total_nonval_callno_used == 0)) {
+ ast_log(LOG_ERROR, "Attempted to decrement total non calltoken validated callnumbers below zero... Callno is:%d \n", callno_entry->callno);
+ }
+
+ if (callno_entry->callno < TRUNK_CALL_START) {
+ ao2_link(callno_pool, callno_entry);
+ } else {
+ ao2_link(callno_pool_trunk, callno_entry);
+ }
+ ao2_ref(callno_entry, -1); /* only container ref remains */
+
+ ao2_unlock(callno_pool);
+ return 0;
+}
+
+static int callno_hash(const void *obj, const int flags)
+{
+ return abs(ast_random());
+}
+
+static int create_callno_pools(void)
+{
+ uint16_t i;
+
+ if (!(callno_pool = ao2_container_alloc(CALLNO_POOL_BUCKETS, callno_hash, NULL))) {
+ return -1;
+ }
+
+ if (!(callno_pool_trunk = ao2_container_alloc(CALLNO_POOL_BUCKETS, callno_hash, NULL))) {
+ return -1;
+ }
+
+ /* start at 2, 0 and 1 are reserved */
+ for (i = 2; i <= IAX_MAX_CALLS; i++) {
+ struct callno_entry *callno_entry;
+
+ if (!(callno_entry = ao2_alloc(sizeof(*callno_entry), NULL))) {
+ return -1;
+ }
+
+ callno_entry->callno = i;
+
+ if (i < TRUNK_CALL_START) {
+ ao2_link(callno_pool, callno_entry);
+ } else {
+ ao2_link(callno_pool_trunk, callno_entry);
+ }
+
+ ao2_ref(callno_entry, -1);
+ }
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Schedules delayed removal of iax2_pvt call number data
+ *
+ * \note After MIN_REUSE_TIME has passed for a destroyed iax2_pvt, the callno is
+ * avaliable again, and the address from the previous connection must be decremented
+ * from the peercnts table. This function schedules these operations to take place.
+ */
+static void sched_delay_remove(struct sockaddr_in *sin, struct callno_entry *callno_entry)
+{
+ int i;
+ struct peercnt *peercnt;
+ struct peercnt tmp = {
+ .addr = sin->sin_addr.s_addr,
+ };
+
+ if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
+ /* refcount is incremented with ao2_find. keep that ref for the scheduler */
+ if (option_debug) {
+ ast_log(LOG_NOTICE, "schedule decrement of callno used for %s in %d seconds\n", ast_inet_ntoa(sin->sin_addr), MIN_REUSE_TIME);
+ }
+ i = iax2_sched_add(sched, MIN_REUSE_TIME * 1000, peercnt_remove_cb, peercnt);
+ if (i == -1) {
+ ao2_ref(peercnt, -1);
+ }
+ }
+
+ iax2_sched_add(sched, MIN_REUSE_TIME * 1000, replace_callno, callno_entry);
+}
+
+/*!
+ * \internal
+ * \brief returns whether or not a frame is capable of starting a new IAX2 dialog.
+ *
+ * \note For this implementation, inbound pokes should _NOT_ be capable of allocating
+ * a new callno.
+ */
+static inline int attribute_pure iax2_allow_new(int frametype, int subclass, int inbound)
+{
+ if (frametype != AST_FRAME_IAX) {
+ return 0;
+ }
+ switch (subclass) {
+ case IAX_COMMAND_NEW:
+ case IAX_COMMAND_REGREQ:
+ case IAX_COMMAND_FWDOWNL:
+ case IAX_COMMAND_REGREL:
+ return 1;
+ case IAX_COMMAND_POKE:
+ if (!inbound) {
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
/*!
* \note Calling this function while holding another pvt lock can cause a deadlock.
*/
@@ -1610,7 +2300,9 @@ static int __find_callno(unsigned short callno, unsigned short dcallno, struct s
{
int res = 0;
int x;
- struct timeval now;
+ /* this call is calltoken validated as long as it is either NEW_FORCE
+ * or NEW_ALLOW_CALLTOKEN_VALIDATED */
+ int validated = (new > NEW_ALLOW) ? 1 : 0;
char host[80];
if (new <= NEW_ALLOW) {
@@ -1707,8 +2399,7 @@ static int __find_callno(unsigned short callno, unsigned short dcallno, struct s
#endif
}
if (!res && (new >= NEW_ALLOW)) {
- int start, found = 0;
-
+ struct callno_entry *callno_entry;
/* It may seem odd that we look through the peer list for a name for
* this *incoming* call. Well, it is weird. However, users don't
* have an IP address/port number that we can match against. So,
@@ -1718,36 +2409,28 @@ static int __find_callno(unsigned short callno, unsigned short dcallno, struct s
if (!iax2_getpeername(*sin, host, sizeof(host)))
snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
- now = ast_tvnow();
- start = 2 + (ast_random() % (TRUNK_CALL_START - 1));
- for (x = start; 1; x++) {
- if (x == TRUNK_CALL_START) {
- x = 1;
- continue;
- }
-
- /* 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, 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;
@@ -1771,6 +2454,7 @@ static int __find_callno(unsigned short callno, unsigned short dcallno, struct s
} else {
ast_log(LOG_WARNING, "Out of resources\n");
ast_mutex_unlock(&iaxsl[x]);
+ replace_callno(callno_entry);
return 0;
}
if (!return_locked)
@@ -2523,6 +3207,10 @@ 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, " Trunk : %s\n", ast_test_flag(peer, IAX_TRUNK) ? "Yes" : "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);
@@ -3332,13 +4020,249 @@ struct parsed_dial_string {
char *options;
};
-static int send_apathetic_reply(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int command, int ts, unsigned char seqno)
+static int send_apathetic_reply(unsigned short callno, unsigned short dcallno,
+ struct sockaddr_in *sin, int command, int ts, unsigned char seqno,
+ int sockfd, struct iax_ie_data *ied)
{
- struct ast_iax2_full_hdr f = { .scallno = htons(0x8000 | callno), .dcallno = htons(dcallno),
- .ts = htonl(ts), .iseqno = seqno, .oseqno = 0, .type = AST_FRAME_IAX,
- .csub = compress_subclass(command) };
+ struct {
+ struct ast_iax2_full_hdr f;
+ struct iax_ie_data ied;
+ } data;
+ size_t size = sizeof(struct ast_iax2_full_hdr);
- return sendto(defaultsockfd, &f, sizeof(f), 0, (struct sockaddr *)sin, sizeof(*sin));
+ if (ied) {
+ size += ied->pos;
+ memcpy(&data.ied, ied->buf, ied->pos);
+ }
+
+ data.f.scallno = htons(0x8000 | callno);
+ data.f.dcallno = htons(dcallno);
+ data.f.ts = htonl(ts);
+ data.f.iseqno = seqno;
+ data.f.oseqno = 0;
+ data.f.type = AST_FRAME_IAX;
+ data.f.csub = compress_subclass(command);
+
+ return sendto(sockfd, &data, size, 0, (struct sockaddr *)sin, sizeof(*sin));
+}
+
+static void add_empty_calltoken_ie(struct chan_iax2_pvt *pvt, struct iax_ie_data *ied)
+{
+ /* first make sure their are two empty bytes left in ied->buf */
+ if (pvt && ied && (2 < ((int) sizeof(ied->buf) - ied->pos))) {
+ ied->buf[ied->pos++] = IAX_IE_CALLTOKEN; /* type */
+ ied->buf[ied->pos++] = 0; /* data size, ZERO in this case */
+ pvt->calltoken_ie_len = 2;
+ }
+}
+
+static void resend_with_token(int callno, struct iax_frame *f, const char *newtoken)
+{
+ struct chan_iax2_pvt *pvt = iaxs[callno];
+ int frametype = f->af.frametype;
+ int subclass = f->af.subclass;
+ struct {
+ struct ast_iax2_full_hdr fh;
+ struct iax_ie_data ied;
+ } data = {
+ .ied.buf = { 0 },
+ .ied.pos = 0,
+ };
+ /* total len - header len gives us the frame's IE len */
+ int ie_data_pos = f->datalen - sizeof(struct ast_iax2_full_hdr);
+
+ if (!pvt) {
+ return; /* this should not be possible if called from socket_process() */
+ }
+
+ /*
+ * Check to make sure last frame sent is valid for call token resend
+ * 1. Frame should _NOT_ be encrypted since it starts the IAX dialog
+ * 2. Frame should _NOT_ already have a destination callno
+ * 3. Frame must be a valid iax_frame subclass capable of starting dialog
+ * 4. Pvt must have a calltoken_ie_len which represents the number of
+ * bytes at the end of the frame used for the previous calltoken ie.
+ * 5. Pvt's calltoken_ie_len must be _LESS_ than the total IE length
+ * 6. Total length of f->data must be _LESS_ than size of our data struct
+ * because f->data must be able to fit within data.
+ */
+ if (f->encmethods || f->dcallno || !iax2_allow_new(frametype, subclass, 0)
+ || !pvt->calltoken_ie_len || (pvt->calltoken_ie_len > ie_data_pos) ||
+ (f->datalen > sizeof(data))) {
+
+ return; /* ignore resend, token was not valid for the dialog */
+ }
+
+ /* token is valid
+ * 1. Copy frame data over
+ * 2. Redo calltoken IE, it will always be the last ie in the frame.
+ * NOTE: Having the ie always be last is not protocol specified,
+ * it is only an implementation choice. Since we only expect the ie to
+ * be last for frames we have sent, this can no way be affected by
+ * another end point.
+ * 3. Remove frame from queue
+ * 4. Free old frame
+ * 5. Clear previous seqnos
+ * 6. Resend with CALLTOKEN ie.
+ */
+
+ /* ---1.--- */
+ memcpy(&data, f->data, f->datalen);
+ data.ied.pos = ie_data_pos;
+
+ /* ---2.--- */
+ /* move to the beginning of the calltoken ie so we can write over it */
+ data.ied.pos -= pvt->calltoken_ie_len;
+ iax_ie_append_str(&data.ied, IAX_IE_CALLTOKEN, newtoken);
+
+ /* make sure to update token length incase it ever has to be stripped off again */
+ pvt->calltoken_ie_len = data.ied.pos - ie_data_pos; /* new pos minus old pos tells how big token ie is */
+
+ /* ---3.--- */
+ AST_LIST_LOCK(&iaxq.queue);
+ AST_LIST_REMOVE(&iaxq.queue, f, list);
+ AST_LIST_UNLOCK(&iaxq.queue);
+
+ /* ---4.--- */
+ iax2_frame_free(f);
+
+ /* ---5.--- */
+ pvt->oseqno = 0;
+ pvt->rseqno = 0;
+ pvt->iseqno = 0;
+ pvt->aseqno = 0;
+ if (pvt->peercallno) {
+ remove_by_peercallno(pvt);
+ pvt->peercallno = 0;
+ }
+
+ /* ---6.--- */
+ send_command(pvt, AST_FRAME_IAX, subclass, 0, data.ied.buf, data.ied.pos, -1);
+}
+
+static void requirecalltoken_mark_auto(const char *name, int subclass)
+{
+ struct iax2_user *user = NULL;
+ struct iax2_peer *peer = NULL;
+
+ if (ast_strlen_zero(name)) {
+ return; /* no username given */
+ }
+
+ if ((subclass == IAX_COMMAND_NEW) && (user = find_user(name)) && (user->calltoken_required == CALLTOKEN_AUTO)) {
+ user->calltoken_required = CALLTOKEN_YES;
+ } else if ((subclass != IAX_COMMAND_NEW) && (peer = find_peer(name, 1)) && (peer->calltoken_required == CALLTOKEN_AUTO)) {
+ peer->calltoken_required = CALLTOKEN_YES;
+ }
+
+ if (peer) {
+ peer_unref(peer);
+ }
+ if (user) {
+ user_unref(user);
+ }
+}
+
+/*!
+ * \internal
+ *
+ * \brief handles calltoken logic for a received iax_frame.
+ *
+ * \note frametype must be AST_FRAME_IAX.
+ *
+ * \note
+ * Three different cases are possible here.
+ * Case 1. An empty calltoken is provided. This means the client supports
+ * calltokens but has not yet received one from us. In this case
+ * a full calltoken IE is created and sent in a calltoken fullframe.
+ * Case 2. A full calltoken is received and must be checked for validity.
+ * Case 3. No calltoken is received indicating that the client does not
+ * support calltokens. In this case it is up to the configuration
+ * to decide how this should be handled (reject or permit without calltoken)
+ */
+static int handle_call_token(struct ast_iax2_full_hdr *fh, struct iax_ies *ies,
+ struct sockaddr_in *sin, int fd)
+{
+#define CALLTOKEN_HASH_FORMAT "%s%d%u%d" /* address + port + ts + randomcalldata */
+#define CALLTOKEN_IE_FORMAT "%u?%s" /* time + ? + (40 char hash) */
+ char buf[256] = { 0 };
+ time_t t = time(NULL);
+ char hash[41]; /* 40 char sha1 hash */
+ int subclass = uncompress_subclass(fh->csub);
+
+ /* ----- Case 1 ----- */
+ if (ies->calltoken && !ies->calltokendata) { /* empty calltoken is provided, client supports calltokens */
+ struct iax_ie_data ied = {
+ .buf = { 0 },
+ .pos = 0,
+ };
+
+ /* create the hash with their address data and our timestamp */
+ snprintf(buf, sizeof(buf), CALLTOKEN_HASH_FORMAT, ast_inet_ntoa(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(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(sin->sin_addr));
+ goto reject; /* received hash does not match ours, reject */
+ } else if ((t < rec_time) || ((t - rec_time) >= MAX_CALLTOKEN_DELAY)) {
+ ast_log(LOG_WARNING, "Too much delay in IAX2 calltoken timestamp from address %s\n", ast_inet_ntoa(sin->sin_addr));
+ goto reject; /* too much delay, reject */
+ }
+
+ /* at this point the call token is valid, returning 0
+ * will allow socket_process to continue as usual */
+ requirecalltoken_mark_auto(ies->username, subclass);
+ return 0;
+
+ /* ----- Case 3 ----- */
+ } else { /* calltokens are not supported for this client, how do we respond? */
+ if (calltoken_required(sin, ies->username, subclass)) {
+ ast_log(LOG_ERROR, "Call rejected, CallToken Support required. If unexpected, resolve by placing address %s in the calltokenignore list or setting user %s requirecalltoken=no\n", ast_inet_ntoa(sin->sin_addr), ies->username);
+ goto reject;
+ }
+ return 0; /* calltoken is not required for this addr, so permit it. */
+ }
+
+reject:
+ /* received frame has failed calltoken inspection, send apathetic reject messages */
+ if (subclass == IAX_COMMAND_REGREQ || subclass == IAX_COMMAND_REGREL) {
+ send_apathetic_reply(1, ntohs(fh->scallno), sin, IAX_COMMAND_REGREJ, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
+ } else {
+ send_apathetic_reply(1, ntohs(fh->scallno), sin, IAX_COMMAND_REJECT, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
+ }
+
+ return 1;
}
/*!
@@ -3538,6 +4462,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]);
@@ -6165,6 +7090,12 @@ static int complete_transfer(int callno, struct iax_ies *ies)
return -1;
}
remove_by_transfercallno(pvt);
+ /* since a transfer has taken place, the address will change.
+ * This must be accounted for in the peercnts table. Remove
+ * the old address and add the new one */
+ peercnt_remove_by_addr(&pvt->addr);
+ peercnt_add(&pvt->transfer);
+ /* now copy over the new address */
memcpy(&pvt->addr, &pvt->transfer, sizeof(pvt->addr));
memset(&pvt->transfer, 0, sizeof(pvt->transfer));
/* Reset sequence numbers */
@@ -6371,6 +7302,8 @@ static void __expire_registry(const void *data)
if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(peer, IAX_TEMPONLY|IAX_RTCACHEFRIENDS)))
realtime_update_peer(peer->name, &peer->addr, 0);
manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
+ /* modify entry in peercnts table as _not_ registered */
+ peercnt_modify(0, 0, &peer->addr);
/* Reset the address */
memset(&peer->addr, 0, sizeof(peer->addr));
/* Reset expiry value */
@@ -6487,8 +7420,13 @@ static int update_registry(struct sockaddr_in *sin, int callno, char *devtype, i
if (inaddrcmp(&p->addr, sin)) {
if (iax2_regfunk)
iax2_regfunk(p->name, 1);
+
+ /* modify entry in peercnts table as _not_ registered */
+ peercnt_modify(0, 0, &p->addr);
+
/* Stash the IP address from which they registered */
memcpy(&p->addr, sin, sizeof(p->addr));
+
snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), p->expiry);
if (!ast_test_flag(p, IAX_TEMPONLY) && sin->sin_addr.s_addr) {
ast_db_put("IAX/Registry", p->name, data);
@@ -6510,7 +7448,12 @@ static int update_registry(struct sockaddr_in *sin, int callno, char *devtype, i
/* Update the host */
/* Verify that the host is really there */
iax2_poke_peer(p, callno);
- }
+ }
+
+ /* modify entry in peercnts table as registered */
+ if (p->maxcallno) {
+ peercnt_modify(1, p->maxcallno, &p->addr);
+ }
/* Make sure our call still exists, an INVAL at the right point may make it go away */
if (!iaxs[callno]) {
@@ -6664,6 +7607,7 @@ static int registry_rerequest(struct iax_ies *ies, int callno, struct sockaddr_i
res = authenticate(challenge, reg->secret, NULL, authmethods, &ied, sin, NULL);
if (!res) {
reg->regstate = REG_STATE_AUTHSENT;
+ add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */
return send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1);
} else
return -1;
@@ -7477,17 +8421,51 @@ static int socket_process(struct iax2_thread *thread)
/* Deal with POKE/PONG without allocating a callno */
if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_POKE) {
/* Reply back with a PONG, but don't care about the result. */
- send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_PONG, ntohl(fh->ts), fh->iseqno + 1);
+ send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_PONG, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
return 1;
} else if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_ACK && dcallno == 1) {
/* Ignore */
return 1;
}
- if ((f.frametype == AST_FRAME_IAX) && ((f.subclass == IAX_COMMAND_NEW) || (f.subclass == IAX_COMMAND_REGREQ) ||
- (f.subclass == IAX_COMMAND_POKE) || (f.subclass == IAX_COMMAND_FWDOWNL) ||
- (f.subclass == IAX_COMMAND_REGREL)))
- new = NEW_ALLOW;
+ f.datalen = res - sizeof(*fh);
+ if (f.datalen) {
+ if (f.frametype == AST_FRAME_IAX) {
+ if (iax_parse_ies(&ies, thread->buf + sizeof(*fh), f.datalen)) {
+ ast_log(LOG_WARNING, "Undecodable frame received from '%s'\n", ast_inet_ntoa(sin.sin_addr));
+ ast_mutex_unlock(&iaxsl[fr->callno]);
+ return 1;
+ }
+ f.data = NULL;
+ f.datalen = 0;
+ } else {
+ f.data = thread->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 (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;
@@ -7513,7 +8491,14 @@ static int socket_process(struct iax2_thread *thread)
check_dcallno = f.frametype == AST_FRAME_IAX ? (f.subclass != IAX_COMMAND_PING && f.subclass != IAX_COMMAND_LAGRQ) : 1;
}
- fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, fd, check_dcallno);
+ if (!(fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, fd, check_dcallno))) {
+ if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_NEW) {
+ send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_REJECT, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
+ } else if (f.frametype == AST_FRAME_IAX && (f.subclass == IAX_COMMAND_REGREQ || f.subclass == IAX_COMMAND_REGREL)) {
+ send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_REGREJ, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
+ }
+ return 1;
+ }
}
if (fr->callno > 0)
@@ -7640,12 +8625,6 @@ static int socket_process(struct iax2_thread *thread)
(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 && thread->buf[res - 1] != '\0') {
if (res < thread->buf_size)
@@ -7653,7 +8632,6 @@ static int socket_process(struct iax2_thread *thread)
else /* Trims one character from the text message, but that's better than overwriting the end of the buffer. */
thread->buf[res - 1] = '\0';
}
- f.datalen = res - sizeof(*fh);
/* Handle implicit ACKing unless this is an INVAL, and only if this is
from the real peer, not the transfer peer */
@@ -7715,25 +8693,6 @@ static int socket_process(struct iax2_thread *thread)
return 1;
}
- if (f.datalen) {
- if (f.frametype == AST_FRAME_IAX) {
- if (iax_parse_ies(&ies, thread->buf + sizeof(*fh), f.datalen)) {
- ast_log(LOG_WARNING, "Undecodable frame received from '%s'\n", ast_inet_ntoa(sin.sin_addr));
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- f.data = NULL;
- f.datalen = 0;
- } else
- f.data = thread->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 */
@@ -8747,6 +9706,26 @@ retryowner2:
return 1;
}
break;
+ case IAX_COMMAND_CALLTOKEN:
+ {
+ struct iax_frame *cur;
+ AST_LIST_LOCK(&iaxq.queue);
+ AST_LIST_TRAVERSE(&iaxq.queue, cur, list) {
+ /* find the last sent frame in our frame queue for this callno.
+ * There are many things to take into account before resending this frame.
+ * All of these are taken care of in resend_with_token() */
+ if (cur->callno == fr->callno) {
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&iaxq.queue);
+
+ /* find last sent frame */
+ if (cur && ies.calltoken && ies.calltokendata) {
+ resend_with_token(fr->callno, cur, (char *) ies.calltokendata);
+ }
+ break;
+ }
default:
if (option_debug)
ast_log(LOG_DEBUG, "Unknown IAX command %d on %d/%d\n", f.subclass, fr->callno, iaxs[fr->callno]->peercallno);
@@ -9053,6 +10032,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;
@@ -9276,7 +10256,12 @@ static int iax2_poke_peer(struct iax2_peer *peer, int heldcall)
/* And send the poke */
ast_mutex_lock(&iaxsl[callno]);
if (iaxs[callno]) {
- send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_POKE, 0, NULL, 0, -1);
+ struct iax_ie_data ied = {
+ .buf = { 0 },
+ .pos = 0,
+ };
+ add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */
+ send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_POKE, 0, ied.buf, ied.pos, -1);
}
ast_mutex_unlock(&iaxsl[callno]);
@@ -9700,6 +10685,9 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
peer->smoothing = 0;
peer->pokefreqok = DEFAULT_FREQ_OK;
peer->pokefreqnotok = DEFAULT_FREQ_NOTOK;
+ peer->maxcallno = 0;
+ peercnt_modify(0, 0, &peer->addr);
+ peer->calltoken_required = CALLTOKEN_DEFAULT;
ast_string_field_set(peer,context,"");
ast_string_field_set(peer,peercontext,"");
ast_clear_flag(peer, IAX_HASCALLERID);
@@ -9853,7 +10841,24 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
ast_string_field_set(peer, zonetag, v->value);
} else if (!strcasecmp(v->name, "adsi")) {
peer->adsi = ast_true(v->value);
- }/* else if (strcasecmp(v->name,"type")) */
+ } else if (!strcasecmp(v->name, "maxcallnumbers")) {
+ if (sscanf(v->value, "%10hu", &peer->maxcallno) != 1) {
+ ast_log(LOG_WARNING, "maxcallnumbers must be set to a valid number. %s is not valid at line %d.\n", v->value, v->lineno);
+ } else {
+ peercnt_modify(1, peer->maxcallno, &peer->addr);
+ }
+ } else if (!strcasecmp(v->name, "requirecalltoken")) {
+ /* default is required unless in optional ip list */
+ if (ast_false(v->value)) {
+ peer->calltoken_required = CALLTOKEN_NO;
+ } else if (!strcasecmp(v->value, "auto")) {
+ peer->calltoken_required = CALLTOKEN_AUTO;
+ } else if (ast_true(v->value)) {
+ peer->calltoken_required = CALLTOKEN_YES;
+ } else {
+ ast_log(LOG_WARNING, "requirecalltoken must be set to a valid value. at line %d\n", v->lineno);
+ }
+ } /* else if (strcasecmp(v->name,"type")) */
/* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
v = v->next;
if (!v) {
@@ -9935,6 +10940,7 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
user->capability = iax2_capability;
user->encmethods = iax2_encryption;
user->adsi = adsi;
+ user->calltoken_required = CALLTOKEN_DEFAULT;
ast_string_field_set(user, name, name);
ast_string_field_set(user, language, language);
ast_copy_flags(user, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_CODEC_USER_FIRST | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP);
@@ -10073,7 +11079,18 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
user->maxauthreq = 0;
} else if (!strcasecmp(v->name, "adsi")) {
user->adsi = ast_true(v->value);
- }/* else if (strcasecmp(v->name,"type")) */
+ } else if (!strcasecmp(v->name, "requirecalltoken")) {
+ /* default is required unless in optional ip list */
+ if (ast_false(v->value)) {
+ user->calltoken_required = CALLTOKEN_NO;
+ } else if (!strcasecmp(v->value, "auto")) {
+ user->calltoken_required = CALLTOKEN_AUTO;
+ } else if (ast_true(v->value)) {
+ user->calltoken_required = CALLTOKEN_YES;
+ } else {
+ ast_log(LOG_WARNING, "requirecalltoken must be set to a valid value. at line %d\n", v->lineno);
+ }
+ } /* else if (strcasecmp(v->name,"type")) */
/* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
v = v->next;
if (!v) {
@@ -10204,10 +11221,12 @@ static void set_config_destroy(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);
}
/*! \brief Load configuration */
-static int set_config(char *config_file, int reload)
+static int set_config(const char *config_file, int reload)
{
struct ast_config *cfg, *ucfg;
int capability=iax2_capability;
@@ -10249,6 +11268,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 = 3;
@@ -10446,7 +11467,19 @@ static int set_config(char *config_file, int reload)
maxauthreq = 0;
} else if (!strcasecmp(v->name, "adsi")) {
adsi = ast_true(v->value);
- } /*else if (strcasecmp(v->name,"type")) */
+ } else if (!strcasecmp(v->name, "maxcallnumbers")) {
+ if (sscanf(v->value, "%10hu", &global_maxcallno) != 1) {
+ ast_log(LOG_WARNING, "maxcallnumbers must be set to a valid number. %s is not valid at line %d\n", v->value, v->lineno);
+ }
+ } else if (!strcasecmp(v->name, "maxcallnumbers_nonvalidated")) {
+ if (sscanf(v->value, "%10hu", &global_maxcallno_nonval) != 1) {
+ ast_log(LOG_WARNING, "maxcallnumbers_nonvalidated must be set to a valid number. %s is not valid at line %d.\n", v->value, v->lineno);
+ }
+ } else if(!strcasecmp(v->name, "calltokenoptional")) {
+ if (add_calltoken_ignore(v->value)) {
+ ast_log(LOG_WARNING, "Invalid calltokenoptional address range - '%s' line %d\n", v->value, v->lineno);
+ }
+ }/*else if (strcasecmp(v->name,"type")) */
/* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
v = v->next;
}
@@ -10537,7 +11570,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), NULL, 0);
if (user) {
@@ -10579,12 +11614,15 @@ static void poke_all_peers(void)
}
static int reload_config(void)
{
- char *config = "iax.conf";
+ static const char config[] = "iax.conf";
struct iax2_registry *reg;
if (set_config(config, 1) > 0) {
prune_peers();
prune_users();
+ ao2_callback(callno_limits, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, prune_addr_range_cb, NULL);
+ ao2_callback(calltoken_ignores, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, prune_addr_range_cb, NULL);
+ ao2_callback(peercnts, OBJ_NODATA, set_peercnt_limit_all_cb, NULL);
AST_LIST_LOCK(&registrations);
AST_LIST_TRAVERSE(&registrations, reg, entry)
iax2_do_register(reg);
@@ -10676,6 +11714,7 @@ static int cache_get_callno_locked(const char *data)
if (pds.key)
ast_string_field_set(iaxs[callno], outkey, pds.key);
/* Start the call going */
+ add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */
send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1);
return callno;
@@ -11110,6 +12149,10 @@ static char show_peer_usage[] =
"Usage: iax2 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";
@@ -11272,6 +12315,10 @@ static struct ast_cli_entry cli_iax2[] = {
iax2_show_peer, "Show details on specific IAX peer",
show_peer_usage, complete_iax2_show_peer },
+ { { "iax2", "show", "callnumber", "usage", NULL },
+ iax2_show_callnumber_usage, "Show current entries in ip Call number limit table",
+ show_callnumber_usage, NULL },
+
{ { "iax2", "set", "debug", NULL },
iax2_do_debug, "Enable IAX debugging",
debug_usage },
@@ -11404,6 +12451,11 @@ static int __unload_module(void)
ao2_ref(users, -1);
ao2_ref(iax_peercallno_pvts, -1);
ao2_ref(iax_transfercallno_pvts, -1);
+ ao2_ref(peercnts, -1);
+ ao2_ref(callno_limits, -1);
+ ao2_ref(calltoken_ignores, -1);
+ ao2_ref(callno_pool, -1);
+ ao2_ref(callno_pool_trunk, -1);
return 0;
}
@@ -11459,35 +12511,77 @@ static int transfercallno_pvt_cmp_cb(void *obj, void *arg, int flags)
return match(&pvt2->transfer, pvt2->transfercallno, pvt2->callno, pvt,
pvt2->frames_received) ? CMP_MATCH | CMP_STOP : 0;
}
-/*! \brief Load IAX2 module, load configuraiton ---*/
-static int load_module(void)
+
+
+static int load_objects(void)
{
- char *config = "iax.conf";
- int res = 0;
- int x;
- struct iax2_registry *reg = NULL;
+ peers = users = iax_peercallno_pvts = iax_transfercallno_pvts = NULL;
+ peercnts = callno_limits = calltoken_ignores = callno_pool = callno_pool_trunk = NULL;
- peers = ao2_container_alloc(MAX_PEER_BUCKETS, peer_hash_cb, peer_cmp_cb);
- if (!peers)
- return AST_MODULE_LOAD_FAILURE;
- users = ao2_container_alloc(MAX_USER_BUCKETS, user_hash_cb, user_cmp_cb);
- if (!users) {
- ao2_ref(peers, -1);
- return AST_MODULE_LOAD_FAILURE;
+ if (!(peers = ao2_container_alloc(MAX_PEER_BUCKETS, peer_hash_cb, peer_cmp_cb))) {
+ goto container_fail;
+ } else if (!(users = ao2_container_alloc(MAX_USER_BUCKETS, user_hash_cb, user_cmp_cb))) {
+ goto container_fail;
+ } else if (!(iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb))) {
+ goto container_fail;
+ } else if (!(iax_transfercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, transfercallno_pvt_hash_cb, transfercallno_pvt_cmp_cb))) {
+ goto container_fail;
+ } else if (!(peercnts = ao2_container_alloc(MAX_PEER_BUCKETS, peercnt_hash_cb, peercnt_cmp_cb))) {
+ goto container_fail;
+ } else if (!(callno_limits = ao2_container_alloc(MAX_PEER_BUCKETS, addr_range_hash_cb, addr_range_cmp_cb))) {
+ goto container_fail;
+ } else if (!(calltoken_ignores = ao2_container_alloc(MAX_PEER_BUCKETS, addr_range_hash_cb, addr_range_cmp_cb))) {
+ goto container_fail;
+ } else if (create_callno_pools()) {
+ goto container_fail;
}
- iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb);
- if (!iax_peercallno_pvts) {
+
+ return 0;
+
+container_fail:
+ if (peers) {
ao2_ref(peers, -1);
- ao2_ref(users, -1);
- return AST_MODULE_LOAD_FAILURE;
}
- iax_transfercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, transfercallno_pvt_hash_cb, transfercallno_pvt_cmp_cb);
- if (!iax_transfercallno_pvts) {
- ao2_ref(peers, -1);
+ if (users) {
ao2_ref(users, -1);
+ }
+ if (iax_peercallno_pvts) {
ao2_ref(iax_peercallno_pvts, -1);
+ }
+ if (iax_transfercallno_pvts) {
+ ao2_ref(iax_transfercallno_pvts, -1);
+ }
+ if (peercnts) {
+ ao2_ref(peercnts, -1);
+ }
+ if (callno_limits) {
+ ao2_ref(callno_limits, -1);
+ }
+ if (calltoken_ignores) {
+ ao2_ref(calltoken_ignores, -1);
+ }
+ if (callno_pool) {
+ ao2_ref(callno_pool, -1);
+ }
+ if (callno_pool_trunk) {
+ ao2_ref(callno_pool_trunk, -1);
+ }
+ return AST_MODULE_LOAD_FAILURE;
+}
+
+/*! \brief Load IAX2 module, load configuraiton ---*/
+static int load_module(void)
+{
+ static const char config[] = "iax.conf";
+ int res = 0;
+ int x;
+ struct iax2_registry *reg = NULL;
+
+ if (load_objects()) {
return AST_MODULE_LOAD_FAILURE;
}
+
+ randomcalltokendata = ast_random();
ast_custom_function_register(&iaxpeer_function);
iax_set_output(iax_debug_output);
diff --git a/channels/iax2-parser.c b/channels/iax2-parser.c
index b99a258b6..fad5a496d 100644
--- a/channels/iax2-parser.c
+++ b/channels/iax2-parser.c
@@ -442,7 +442,9 @@ void iax_frame_subclass2str(int subclass, char *str, size_t len)
"PROVISN",
"FWDWNLD",
"FWDATA ",
- "TXMEDIA"
+ "TXMEDIA",
+ "RTKEY ",
+ "CTOKEN ",
};
if ((copylen > len) || !subclass || (subclass < 0)) {
str[0] = '\0';
@@ -959,6 +961,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);
diff --git a/channels/iax2-parser.h b/channels/iax2-parser.h
index e6c5ece99..914b91f66 100644
--- a/channels/iax2-parser.h
+++ b/channels/iax2-parser.h
@@ -74,6 +74,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
diff --git a/channels/iax2.h b/channels/iax2.h
index 960dec8bb..fa5e39193 100644
--- a/channels/iax2.h
+++ b/channels/iax2.h
@@ -73,6 +73,8 @@
#define IAX_COMMAND_FWDOWNL 36 /* Download firmware */
#define IAX_COMMAND_FWDATA 37 /* Firmware Data */
#define IAX_COMMAND_TXMEDIA 38 /* Transfer media only */
+#define IAX_COMMAND_CALLTOKEN 40 /*! Call number token */
+
#define IAX_DEFAULT_REG_EXPIRE 60 /* By default require re-registration once per minute */
@@ -133,6 +135,7 @@
#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)
diff --git a/configs/iax.conf.sample b/configs/iax.conf.sample
index 9fae9e970..b99fa30dd 100644
--- a/configs/iax.conf.sample
+++ b/configs/iax.conf.sample
@@ -268,6 +268,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.
;
diff --git a/include/asterisk/acl.h b/include/asterisk/acl.h
index f9114ce11..d3e7a4f5a 100644
--- a/include/asterisk/acl.h
+++ b/include/asterisk/acl.h
@@ -35,11 +35,17 @@ extern "C" {
#define AST_SENSE_ALLOW 1
/* Host based access control */
-
-struct ast_ha;
+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);
-struct ast_ha *ast_append_ha(char *sense, char *stuff, struct ast_ha *path);
+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);
int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin);
int ast_get_ip(struct sockaddr_in *sin, const char *value);
int ast_get_ip_or_srv(struct sockaddr_in *sin, const char *value, const char *service);
diff --git a/include/asterisk/astobj2.h b/include/asterisk/astobj2.h
index ec841b888..91836b9e7 100644
--- a/include/asterisk/astobj2.h
+++ b/include/asterisk/astobj2.h
@@ -290,6 +290,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/main/acl.c b/main/acl.c
index 8f388a3d7..2cfccf1d3 100644
--- a/main/acl.c
+++ b/main/acl.c
@@ -72,14 +72,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/lock.h"
#include "asterisk/srv.h"
-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 = { .s_addr = 0x00000000, };
@@ -261,7 +253,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));
@@ -303,7 +295,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;
char *nm = "255.255.255.255";
diff --git a/main/astobj2.c b/main/astobj2.c
index 8157c78cf..991fd80fe 100644
--- a/main/astobj2.c
+++ b/main/astobj2.c
@@ -453,7 +453,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 */
@@ -483,13 +483,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;
@@ -545,6 +547,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;