Compare commits

..

3 Commits

Author SHA1 Message Date
Kevin P. Fleming
b67ea916d7 Convert all release tags to Opsound music-on-hold.
For more details:
http://blogs.digium.com/2009/08/18/asterisk-music-on-hold-changes/



git-svn-id: https://origsvn.digium.com/svn/asterisk/tags/1.2.28@212958 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2009-08-18 20:42:51 +00:00
Russell Bryant
d0c1e9575c Importing files for 1.2.28 release
git-svn-id: https://origsvn.digium.com/svn/asterisk/tags/1.2.28@114563 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-04-22 22:23:10 +00:00
Russell Bryant
80ba87c0fb Creating tag for the release of asterisk-1.2.28
git-svn-id: https://origsvn.digium.com/svn/asterisk/tags/1.2.28@114562 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-04-22 22:22:25 +00:00
11 changed files with 180 additions and 1873 deletions

View File

@@ -1 +1 @@
1.2.31
1.2.28

173
ChangeLog
View File

@@ -1,176 +1,3 @@
2009-01-06 Leif Madsen <lmadsen@digium.com>
* Asterisk 1.2.31 released
2009-01-06 20:44 +0000 [r167259] Tilghman Lesher <tlesher@digium.com>
* channels/chan_iax2.c: Security fix AST-2009-001.
2008-12-10 Tilghman Lesher <tlesher@digium.com>
* Asterisk 1.2.30.4 released
2008-12-10 21:06 +0000 [r162868] Tilghman Lesher <tlesher@digium.com>
* channels/chan_iax2.c: Fix for AST-2008-012
2008-12-05 20:50 +0000 [r161421] Sean Bright <sean.bright@gmail.com>
* include/asterisk/astobj2.h, astobj2.c: Fix build errors on
FreeBSD (uint -> unsigned int). (closes issue #14006) Reported
by: alphaque Patches: astobj2.h-patch uploaded by alphaque
(license 259) (Slightly modified by seanbright)
2008-12-01 Tilghman Lesher <tlesher@digium.com>
* Asterisk 1.2.30.3 released
2008-11-25 21:37 +0000 [r159245] Tilghman Lesher <tlesher@digium.com>
* channels/chan_iax2.c: Regression fix for last security fix. Set
the iseqno correctly. (closes issue #13918) Reported by:
ffloimair Patches: 20081119__bug13918.diff.txt uploaded by
Corydon76 (license 14) Tested by: ffloimair
2008-08-09 Tilghman Lesher <tlesher@digium.com>
* Asterisk 1.2.30.2 released
2008-08-09 15:24 +0000 [r136945] Tilghman Lesher <tlesher@digium.com>
* include/asterisk/compat.h, include/asterisk/astobj2.h: Regression
fixes for Solaris
2008-07-25 15:00 +0000 [r133577] Russell Bryant <russell@digium.com>
* LICENSE: Fix the IAX2 URI for calling Digium
2008-07-23 Tilghman Lesher <tlesher@digium.com>
* Asterisk 1.2.30.1 released
2008-07-24 03:46 +0000 [r133360] Tilghman Lesher <tlesher@digium.com>
* channels/chan_iax2.c: This part was not correctly patched for
AST-2008-010.
2008-07-22 Russell Bryant <russell@digium.com>
* Asterisk 1.2.30 released
2008-07-22 21:14 +0000 [r132711] Tilghman Lesher <tlesher@digium.com>
* configs/iax.conf.sample, channels/chan_iax2.c: Fixes for
AST-2008-010 and AST-2008-011
2008-06-03 Russell Bryant <russell@digium.com>
* Asterisk 1.2.29 released
2008-06-03 19:30 +0000 [r120109] Joshua Colp <jcolp@digium.com>
* channels/chan_sip.c: Copy the From header into a variable so that
pedantic SIP handling does not try to mess with a NULL pointer.
(AST-2008-008) (closes issue #12607) Reported by: hooi
2008-05-30 12:49 +0000 [r119008-119237] Russell Bryant <russell@digium.com>
* channels/chan_iax2.c: - Instead of only enforcing destination
call number checking on an ACK, check all full frames except for
PING and LAGRQ, which may be sent by older versions too quickly
to contain the destination call number. (As suggested by Tim
Panton on the asterisk-dev list) - Merge changes from
team/russell/iax2-frame-race, which prevents PING and LAGRQ from
being sent before the destination call number is known.
* channels/chan_iax2.c: Merge changes from
team/russell/iax2-another-fix-to-the-fix As described in the
following post to the asterisk-dev mailing list, only enforce
destination call numbers when processing an ACK.
http://lists.digium.com/pipermail/asterisk-dev/2008-May/033217.html
2008-05-21 Russell Bryant <russell@digium.com>
* Asterisk 1.2.28.1 released
2008-05-08 19:14 +0000 [r115564] Russell Bryant <russell@digium.com>
* channels/chan_iax2.c: Fix a race condition that bbryant just
found while doing some IAX2 testing. He was running Asterisk
trunk running IAX2 calls through a few Asterisk boxes, however,
the audio was extremely choppy. We looked at a packet trace and
saw a storm of INVAL and VNAK frames being sent from one box to
another. It turned out that what had happened was that one box
tried to send a CONTROL frame before the 3 way handshake had
completed. So, that frame did not include the destination call
number, because it didn't have it yet. Part of our recent work
for security issues included an additional check to ensure that
frames that are supposed to include the destination call number
have the correct one. This caused the frame to be rejected with
an INVAL. The frame would get retransmitted for forever, rejected
every time ... This race condition exists in all versions that
got the security changes, in theory. However, it is really only
likely that this would cause a problem in Asterisk trunk. There
was a control frame being sent (SRCUPDATE) at the _very_
beginning of the call, which does not exist in 1.2 or 1.4.
However, I am fixing all versions that could potentially be
affected by the introduced race condition. These changes are what
bbryant and I came up with to fix the issue. Instead of simply
dropping control frames that get sent before the handshake is
complete, the code attempts to wait a little while, since in most
cases, the handshake will complete very quickly. If it doesn't
complete after yielding for a little while, then the frame gets
dropped.
2008-05-07 16:22 +0000 [r115511] Russell Bryant <russell@digium.com>
* include/asterisk/dlinkedlists.h (removed), channels/chan_iax2.c:
Remove remnants of dlinkedlists. I didn't actually use them in
the final version of my IAX2 improvements.
2008-05-06 19:54 +0000 [r115421] Jason Parker <jparker@digium.com>
* contrib/scripts/get_ilbc_source.sh: read requires an argument on
some non-bash shells (closes issue #12593) Reported by: bkruse
Patches: getilbc.sh_12593_v1.diff uploaded by bkruse (license
132)
2008-05-05 17:53 +0000 [r115296] Russell Bryant <russell@digium.com>
* Makefile, include/asterisk/astobj2.h (added), astobj2.c (added),
include/asterisk/dlinkedlists.h (added), channels/chan_iax2.c:
Merge changes from team/russell/iax2_find_callno_1.2 These
changes address a critical performance issue introduced in the
latest release. The fix for the latest security issue included a
change that made Asterisk randomly choose call numbers to make
them more difficult to guess by attackers. However, due to some
inefficient (this is by far, an understatement) code, when
Asterisk chose high call numbers, chan_iax2 became unusable after
just a small number of calls. On a small embedded platform, it
would not be able to handle a single call. On my Intel Core 2 Duo
@ 2.33 GHz, I couldn't run more than about 16 IAX2 channels.
Ouch. These changes address some performance issues of the
find_callno() function that have bothered me for a very long
time. On every incoming media frame, it iterated through every
possible call number trying to find a matching active call. This
involved a mutex lock and unlock for each call number checked.
So, if the random call number chosen was 20000, then every media
frame would cause 20000 locks and unlocks. Previously, this
problem was not as obvious since Asterisk always chose the lowest
call number it could. A second container for IAX2 pvt structs has
been added. It is an astobj2 hash table. When we know the remote
side's call number, the pvt goes into the hash table with a hash
value of the remote side's call number. Then, lookups for
incoming media frames are a very fast hash lookup instead of an
absolutely insane array traversal. In a quick test, I was able to
get more than 3600% more IAX2 channels on my machine with these
changes.
2008-04-29 12:52 +0000 [r114822] Kevin P. Fleming <kpfleming@digium.com>
* contrib/scripts/get_ilbc_source.sh: stop script from appending
source code if run multiple times
2008-04-22 Russell Bryant <russell@digium.com>
* Asterisk 1.2.28 released

View File

@@ -58,7 +58,7 @@ contact us:
+1.877.546.8963 (via telephone in the USA)
+1.256.428.6000 (via telephone outside the USA)
+1.256.864.0464 (via FAX inside or outside the USA)
IAX2/pbx.digium.com (via IAX2)
IAX2/misery.digium.com/6000 (via IAX2)
licensing@digium.com (via email)
Digium, Inc.

View File

@@ -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
ifeq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/sys/poll.h),)
OBJS+= poll.o

722
astobj2.c
View File

@@ -1,722 +0,0 @@
/*
* astobj2 - replacement containers for asterisk data structures.
*
* Copyright (C) 2006 Marta Carbone, Luigi Rizzo - Univ. di Pisa, Italy
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*
* Function implementing astobj2 objects.
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <stdlib.h>
#include "asterisk/astobj2.h"
#include "asterisk/utils.h"
#include "asterisk/cli.h"
#include "asterisk/linkedlists.h"
/*!
* astobj2 objects are always prepended this data structure,
* which contains a lock, a reference counter,
* the flags and a pointer to a destructor.
* The refcount is used to decide when it is time to
* invoke the destructor.
* The magic number is used for consistency check.
* XXX the lock is not always needed, and its initialization may be
* expensive. Consider making it external.
*/
struct __priv_data {
ast_mutex_t lock;
int ref_counter;
ao2_destructor_fn destructor_fn;
/*! for stats */
size_t data_size;
/*! magic number. This is used to verify that a pointer passed in is a
* valid astobj2 */
uint32_t magic;
};
#define AO2_MAGIC 0xa570b123
/*!
* What an astobj2 object looks like: fixed-size private data
* followed by variable-size user data.
*/
struct astobj2 {
struct __priv_data priv_data;
void *user_data[0];
};
#ifdef AST_DEVMODE
#define AO2_DEBUG 1
#endif
#ifdef AO2_DEBUG
struct ao2_stats {
volatile int total_objects;
volatile int total_mem;
volatile int total_containers;
volatile int total_refs;
volatile int total_locked;
};
static struct ao2_stats ao2;
#endif
#ifndef HAVE_BKTR /* backtrace support */
void ao2_bt(void) {}
#else
#include <execinfo.h> /* for backtrace */
void ao2_bt(void)
{
int c, i;
#define N1 20
void *addresses[N1];
char **strings;
c = backtrace(addresses, N1);
strings = backtrace_symbols(addresses,c);
ast_verbose("backtrace returned: %d\n", c);
for(i = 0; i < c; i++) {
ast_verbose("%d: %p %s\n", i, addresses[i], strings[i]);
}
free(strings);
}
#endif
/*!
* \brief convert from a pointer _p to a user-defined object
*
* \return the pointer to the astobj2 structure
*/
static inline struct astobj2 *INTERNAL_OBJ(void *user_data)
{
struct astobj2 *p;
if (!user_data) {
ast_log(LOG_ERROR, "user_data is NULL\n");
return NULL;
}
p = (struct astobj2 *) ((char *) user_data - sizeof(*p));
if (AO2_MAGIC != (p->priv_data.magic) ) {
ast_log(LOG_ERROR, "bad magic number 0x%x for %p\n", p->priv_data.magic, p);
p = NULL;
}
return p;
}
/*!
* \brief convert from a pointer _p to an astobj2 object
*
* \return the pointer to the user-defined portion.
*/
#define EXTERNAL_OBJ(_p) ((_p) == NULL ? NULL : (_p)->user_data)
int ao2_lock(void *user_data)
{
struct astobj2 *p = INTERNAL_OBJ(user_data);
if (p == NULL)
return -1;
#ifdef AO2_DEBUG
ast_atomic_fetchadd_int(&ao2.total_locked, 1);
#endif
return ast_mutex_lock(&p->priv_data.lock);
}
int ao2_unlock(void *user_data)
{
struct astobj2 *p = INTERNAL_OBJ(user_data);
if (p == NULL)
return -1;
#ifdef AO2_DEBUG
ast_atomic_fetchadd_int(&ao2.total_locked, -1);
#endif
return ast_mutex_unlock(&p->priv_data.lock);
}
/*
* The argument is a pointer to the user portion.
*/
int ao2_ref(void *user_data, const int delta)
{
int current_value;
int ret;
struct astobj2 *obj = INTERNAL_OBJ(user_data);
if (obj == NULL)
return -1;
/* if delta is 0, just return the refcount */
if (delta == 0)
return (obj->priv_data.ref_counter);
/* we modify with an atomic operation the reference counter */
ret = ast_atomic_fetchadd_int(&obj->priv_data.ref_counter, delta);
current_value = ret + delta;
#ifdef AO2_DEBUG
ast_atomic_fetchadd_int(&ao2.total_refs, delta);
#endif
/* this case must never happen */
if (current_value < 0)
ast_log(LOG_ERROR, "refcount %d on object %p\n", current_value, user_data);
if (current_value <= 0) { /* last reference, destroy the object */
if (obj->priv_data.destructor_fn != NULL)
obj->priv_data.destructor_fn(user_data);
ast_mutex_destroy(&obj->priv_data.lock);
#ifdef AO2_DEBUG
ast_atomic_fetchadd_int(&ao2.total_mem, - obj->priv_data.data_size);
ast_atomic_fetchadd_int(&ao2.total_objects, -1);
#endif
/* for safety, zero-out the astobj2 header and also the
* first word of the user-data, which we make sure is always
* allocated. */
bzero(obj, sizeof(struct astobj2 *) + sizeof(void *) );
free(obj);
}
return ret;
}
/*
* We always alloc at least the size of a void *,
* for debugging purposes.
*/
void *ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn)
{
/* allocation */
struct astobj2 *obj;
if (data_size < sizeof(void *))
data_size = sizeof(void *);
obj = calloc(1, sizeof(*obj) + data_size);
if (obj == NULL)
return NULL;
ast_mutex_init(&obj->priv_data.lock);
obj->priv_data.magic = AO2_MAGIC;
obj->priv_data.data_size = data_size;
obj->priv_data.ref_counter = 1;
obj->priv_data.destructor_fn = destructor_fn; /* can be NULL */
#ifdef AO2_DEBUG
ast_atomic_fetchadd_int(&ao2.total_objects, 1);
ast_atomic_fetchadd_int(&ao2.total_mem, data_size);
ast_atomic_fetchadd_int(&ao2.total_refs, 1);
#endif
/* return a pointer to the user data */
return EXTERNAL_OBJ(obj);
}
/* internal callback to destroy a container. */
static void container_destruct(void *c);
/* each bucket in the container is a tailq. */
AST_LIST_HEAD_NOLOCK(bucket, bucket_list);
/*!
* A container; stores the hash and callback functions, information on
* the size, the hash bucket heads, and a version number, starting at 0
* (for a newly created, empty container)
* and incremented every time an object is inserted or deleted.
* The assumption is that an object is never moved in a container,
* but removed and readded with the new number.
* The version number is especially useful when implementing iterators.
* In fact, we can associate a unique, monotonically increasing number to
* each object, which means that, within an iterator, we can store the
* version number of the current object, and easily look for the next one,
* which is the next one in the list with a higher number.
* Since all objects have a version >0, we can use 0 as a marker for
* 'we need the first object in the bucket'.
*
* \todo Linking and unlink objects is typically expensive, as it
* involves a malloc() of a small object which is very inefficient.
* To optimize this, we allocate larger arrays of bucket_list's
* when we run out of them, and then manage our own freelist.
* This will be more efficient as we can do the freelist management while
* we hold the lock (that we need anyways).
*/
struct ao2_container {
ao2_hash_fn hash_fn;
ao2_callback_fn cmp_fn;
int n_buckets;
/*! Number of elements in the container */
int elements;
/*! described above */
int version;
/*! variable size */
struct bucket buckets[0];
};
/*!
* \brief always zero hash function
*
* it is convenient to have a hash function that always returns 0.
* This is basically used when we want to have a container that is
* a simple linked list.
*
* \returns 0
*/
static int hash_zero(const void *user_obj, const int flags)
{
return 0;
}
/*
* A container is just an object, after all!
*/
struct ao2_container *
ao2_container_alloc(const unsigned int n_buckets, ao2_hash_fn hash_fn,
ao2_callback_fn cmp_fn)
{
/* XXX maybe consistency check on arguments ? */
/* compute the container size */
size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
struct ao2_container *c = ao2_alloc(container_size, container_destruct);
if (!c)
return NULL;
c->version = 1; /* 0 is a reserved value here */
c->n_buckets = n_buckets;
c->hash_fn = hash_fn ? hash_fn : hash_zero;
c->cmp_fn = cmp_fn;
#ifdef AO2_DEBUG
ast_atomic_fetchadd_int(&ao2.total_containers, 1);
#endif
return c;
}
/*!
* return the number of elements in the container
*/
int ao2_container_count(struct ao2_container *c)
{
return c->elements;
}
/*!
* A structure to create a linked list of entries,
* used within a bucket.
* XXX \todo this should be private to the container code
*/
struct bucket_list {
AST_LIST_ENTRY(bucket_list) entry;
int version;
struct astobj2 *astobj; /* pointer to internal data */
};
/*
* link an object to a container
*/
void *__ao2_link(struct ao2_container *c, void *user_data, int iax2_hack)
{
int i;
/* create a new list entry */
struct bucket_list *p;
struct astobj2 *obj = INTERNAL_OBJ(user_data);
if (!obj)
return NULL;
if (INTERNAL_OBJ(c) == NULL)
return NULL;
p = calloc(1, sizeof(*p));
if (!p)
return NULL;
i = c->hash_fn(user_data, OBJ_POINTER);
ao2_lock(c);
i %= c->n_buckets;
p->astobj = obj;
p->version = ast_atomic_fetchadd_int(&c->version, 1);
if (iax2_hack)
AST_LIST_INSERT_HEAD(&c->buckets[i], p, entry);
else
AST_LIST_INSERT_TAIL(&c->buckets[i], p, entry);
ast_atomic_fetchadd_int(&c->elements, 1);
ao2_ref(user_data, +1);
ao2_unlock(c);
return p;
}
/*!
* \brief another convenience function is a callback that matches on address
*/
int ao2_match_by_addr(void *user_data, void *arg, int flags)
{
return (user_data == arg) ? (CMP_MATCH | CMP_STOP) : 0;
}
/*
* Unlink an object from the container
* and destroy the associated * ao2_bucket_list structure.
*/
void *ao2_unlink(struct ao2_container *c, void *user_data)
{
if (INTERNAL_OBJ(user_data) == NULL) /* safety check on the argument */
return NULL;
ao2_callback(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data);
return NULL;
}
/*!
* \brief special callback that matches all
*/
static int cb_true(void *user_data, void *arg, int flags)
{
return CMP_MATCH;
}
/*!
* Browse the container using different stategies accoding the flags.
* \return Is a pointer to an object or to a list of object if OBJ_MULTIPLE is
* specified.
*/
void *ao2_callback(struct ao2_container *c,
const enum search_flags flags,
ao2_callback_fn cb_fn, void *arg)
{
int i, last; /* search boundaries */
void *ret = NULL;
if (INTERNAL_OBJ(c) == NULL) /* safety check on the argument */
return NULL;
if ((flags & (OBJ_MULTIPLE | OBJ_NODATA)) == OBJ_MULTIPLE) {
ast_log(LOG_WARNING, "multiple data return not implemented yet (flags %x)\n", flags);
return NULL;
}
/* override the match function if necessary */
#if 0
/* Removing this slightly changes the meaning of OBJ_POINTER, but makes it
* do what I want it to. I'd like to hint to ao2_callback that the arg is
* of the same object type, so it can be passed to the hash function.
* However, I don't want to imply that this is the object being searched for. */
if (flags & OBJ_POINTER)
cb_fn = match_by_addr;
else
#endif
if (cb_fn == NULL) /* if NULL, match everything */
cb_fn = cb_true;
/*
* XXX this can be optimized.
* If we have a hash function and lookup by pointer,
* run the hash function. Otherwise, scan the whole container
* (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;
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;
last = c->n_buckets;
} else {
last = i + 1;
}
ao2_lock(c); /* avoid modifications to the content */
for (; i < last ; i++) {
/* scan the list with prev-cur pointers */
struct bucket_list *cur;
AST_LIST_TRAVERSE_SAFE_BEGIN(&c->buckets[i], cur, entry) {
int match = cb_fn(EXTERNAL_OBJ(cur->astobj), arg, flags) & (CMP_MATCH | CMP_STOP);
/* we found the object, performing operations according flags */
if (match == 0) { /* no match, no stop, continue */
continue;
} else if (match == CMP_STOP) { /* no match but stop, we are done */
i = last;
break;
}
/* we have a match (CMP_MATCH) here */
if (!(flags & OBJ_NODATA)) { /* if must return the object, record the value */
/* it is important to handle this case before the unlink */
ret = EXTERNAL_OBJ(cur->astobj);
ao2_ref(ret, 1);
}
if (flags & OBJ_UNLINK) { /* must unlink */
struct bucket_list *x = cur;
/* we are going to modify the container, so update version */
ast_atomic_fetchadd_int(&c->version, 1);
AST_LIST_REMOVE_CURRENT(&c->buckets[i], entry);
/* update number of elements and version */
ast_atomic_fetchadd_int(&c->elements, -1);
ao2_ref(EXTERNAL_OBJ(x->astobj), -1);
free(x); /* free the link record */
}
if ((match & CMP_STOP) || (flags & OBJ_MULTIPLE) == 0) {
/* We found the only match we need */
i = last; /* force exit from outer loop */
break;
}
if (!(flags & OBJ_NODATA)) {
#if 0 /* XXX to be completed */
/*
* This is the multiple-return case. We need to link
* the object in a list. The refcount is already increased.
*/
#endif
}
}
AST_LIST_TRAVERSE_SAFE_END
}
ao2_unlock(c);
return ret;
}
/*!
* the find function just invokes the default callback with some reasonable flags.
*/
void *ao2_find(struct ao2_container *c, void *arg, enum search_flags flags)
{
return ao2_callback(c, flags, c->cmp_fn, arg);
}
/*!
* initialize an iterator so we start from the first object
*/
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags)
{
struct ao2_iterator a = {
.c = c,
.flags = flags
};
return a;
}
/*
* move to the next element in the container.
*/
void * ao2_iterator_next(struct ao2_iterator *a)
{
int lim;
struct bucket_list *p = NULL;
void *ret = NULL;
if (INTERNAL_OBJ(a->c) == NULL)
return NULL;
if (!(a->flags & F_AO2I_DONTLOCK))
ao2_lock(a->c);
/* optimization. If the container is unchanged and
* we have a pointer, try follow it
*/
if (a->c->version == a->c_version && (p = a->obj) ) {
if ( (p = AST_LIST_NEXT(p, entry)) )
goto found;
/* nope, start from the next bucket */
a->bucket++;
a->version = 0;
a->obj = NULL;
}
lim = a->c->n_buckets;
/* Browse the buckets array, moving to the next
* buckets if we don't find the entry in the current one.
* Stop when we find an element with version number greater
* than the current one (we reset the version to 0 when we
* switch buckets).
*/
for (; a->bucket < lim; a->bucket++, a->version = 0) {
/* scan the current bucket */
AST_LIST_TRAVERSE(&a->c->buckets[a->bucket], p, entry) {
if (p->version > a->version)
goto found;
}
}
found:
if (p) {
a->version = p->version;
a->obj = p;
a->c_version = a->c->version;
ret = EXTERNAL_OBJ(p->astobj);
/* inc refcount of returned object */
ao2_ref(ret, 1);
}
if (!(a->flags & F_AO2I_DONTLOCK))
ao2_unlock(a->c);
return ret;
}
/* callback for destroying container.
* we can make it simple as we know what it does
*/
static int cd_cb(void *obj, void *arg, int flag)
{
ao2_ref(obj, -1);
return 0;
}
static void container_destruct(void *_c)
{
struct ao2_container *c = _c;
ao2_callback(c, OBJ_UNLINK, cd_cb, NULL);
#ifdef AO2_DEBUG
ast_atomic_fetchadd_int(&ao2.total_containers, -1);
#endif
}
#ifdef AO2_DEBUG
static int print_cb(void *obj, void *arg, int flag)
{
int *fd = arg;
char *s = (char *)obj;
ast_cli(*fd, "string <%s>\n", s);
return 0;
}
/*
* Print stats
*/
static int handle_astobj2_stats(int fd, int argc, char *argv[])
{
ast_cli(fd, "Objects : %d\n", ao2.total_objects);
ast_cli(fd, "Containers : %d\n", ao2.total_containers);
ast_cli(fd, "Memory : %d\n", ao2.total_mem);
ast_cli(fd, "Locked : %d\n", ao2.total_locked);
ast_cli(fd, "Refs : %d\n", ao2.total_refs);
return 0;
}
/*
* This is testing code for astobj
*/
static int handle_astobj2_test(int fd, int argc, char *argv[])
{
struct ao2_container *c1;
int i, lim;
char *obj;
static int prof_id = -1;
if (prof_id == -1)
prof_id = ast_add_profile("ao2_alloc", 0);
ast_cli(fd, "argc %d argv %s %s %s\n", argc, argv[0], argv[1], argv[2]);
lim = atoi(argv[2]);
ast_cli(fd, "called astobj_test\n");
handle_astobj2_stats(fd, 0, NULL);
/*
* allocate a container with no default callback, and no hash function.
* No hash means everything goes in the same bucket.
*/
c1 = ao2_container_alloc(100, NULL /* no callback */, NULL /* no hash */);
ast_cli(fd, "container allocated as %p\n", c1);
/*
* fill the container with objects.
* ao2_alloc() gives us a reference which we pass to the
* container when we do the insert.
*/
for (i = 0; i < lim; i++) {
ast_mark(prof_id, 1 /* start */);
obj = ao2_alloc(80, NULL);
ast_mark(prof_id, 0 /* stop */);
ast_cli(fd, "object %d allocated as %p\n", i, obj);
sprintf(obj, "-- this is obj %d --", i);
ao2_link(c1, obj);
}
ast_cli(fd, "testing callbacks\n");
ao2_callback(c1, 0, print_cb, &fd);
ast_cli(fd, "testing iterators, remove every second object\n");
{
struct ao2_iterator ai;
int x = 0;
ai = ao2_iterator_init(c1, 0);
while ( (obj = ao2_iterator_next(&ai)) ) {
ast_cli(fd, "iterator on <%s>\n", obj);
if (x++ & 1)
ao2_unlink(c1, obj);
ao2_ref(obj, -1);
}
ast_cli(fd, "testing iterators again\n");
ai = ao2_iterator_init(c1, 0);
while ( (obj = ao2_iterator_next(&ai)) ) {
ast_cli(fd, "iterator on <%s>\n", obj);
ao2_ref(obj, -1);
}
}
ast_cli(fd, "testing callbacks again\n");
ao2_callback(c1, 0, print_cb, &fd);
ast_verbose("now you should see an error message:\n");
ao2_ref(&i, -1); /* i is not a valid object so we print an error here */
ast_cli(fd, "destroy container\n");
ao2_ref(c1, -1); /* destroy container */
handle_astobj2_stats(fd, 0, NULL);
return 0;
}
static struct ast_cli_entry cli_astobj2[] = {
{ { "astobj2", "stats", NULL },
handle_astobj2_stats, "Print astobj2 statistics", },
{ { "astobj2", "test", NULL } , handle_astobj2_test, "Test astobj2", },
};
#endif /* AO2_DEBUG */
int astobj2_init(void)
{
#ifdef AO2_DEBUG
ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
#endif
return 0;
}

View File

@@ -89,7 +89,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/dnsmgr.h"
#include "asterisk/devicestate.h"
#include "asterisk/netsock.h"
#include "asterisk/astobj2.h"
#include "iax2.h"
#include "iax2-parser.h"
@@ -164,7 +163,6 @@ static int trunkfreq = 20;
static int authdebug = 1;
static int autokill = 0;
static int iaxcompat = 0;
static int lastauthmethod = 0;
static int iaxdefaultdpcache=10 * 60; /* Cache dialplan entries for 10 minutes by default */
@@ -273,7 +271,6 @@ enum {
IAX_DELAYPBXSTART = (1 << 25), /*!< Don't start a PBX on the channel until the peer sends us a
response, so that we've achieved a three-way handshake with
them before sending voice or anything else*/
IAX_ALLOWFWDOWNLOAD = (1 << 26), /*!< Allow the FWDOWNL command? */
} iax2_flags;
static int global_rtautoclear = 120;
@@ -739,16 +736,6 @@ 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
*
* Active IAX2 pvt structs are also stored in this container, if they are a part
* of an active call where we know the remote side's call number. The reason
* for this is that incoming media frames do not contain our call number. So,
* instead of having to iterate the entire iaxs array, we use this container to
* look up calls where the remote side is using a given call number.
*/
static struct ao2_container *iax_peercallno_pvts;
static int send_command(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
static int send_command_locked(unsigned short callno, char, int, unsigned int, const unsigned char *, int, int);
@@ -808,27 +795,15 @@ static const struct ast_channel_tech iax2_tech = {
static int send_ping(void *data)
{
int callno = (long)data;
int res = 0;
/* Ping only if it's real, not if it's bridged */
ast_mutex_lock(&iaxsl[callno]);
while (iaxs[callno]) {
res = 1;
if (!iaxs[callno]->peercallno) {
break;
}
if (iaxs[callno]) {
#ifdef BRIDGE_OPTIMIZATION
if (!iaxs[callno]->bridgecallno)
#endif
send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_PING, 0, NULL, 0, -1);
break;
}
ast_mutex_unlock(&iaxsl[callno]);
return res;
return 1;
} else
return 0;
}
static int get_encrypt_methods(const char *s)
@@ -846,27 +821,15 @@ static int get_encrypt_methods(const char *s)
static int send_lagrq(void *data)
{
int callno = (long)data;
int res = 0;
/* Ping only if it's real not if it's bridged */
ast_mutex_lock(&iaxsl[callno]);
while (iaxs[callno]) {
res = 1;
if (!iaxs[callno]->peercallno) {
break;
}
if (iaxs[callno]) {
#ifdef BRIDGE_OPTIMIZATION
if (!iaxs[callno]->bridgecallno)
#endif
send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_LAGRQ, 0, NULL, 0, -1);
break;
}
ast_mutex_unlock(&iaxsl[callno]);
return res;
return 1;
} else
return 0;
}
static unsigned char compress_subclass(int subclass)
@@ -950,188 +913,17 @@ static int iax2_getpeername(struct sockaddr_in sin, char *host, int len, int loc
return res;
}
static void update_max_trunk(void)
{
int max = TRUNK_CALL_START;
int x;
/* XXX Prolly don't need locks here XXX */
for (x=TRUNK_CALL_START;x<IAX_MAX_CALLS - 1; x++) {
if (iaxs[x])
max = x + 1;
}
maxtrunkcall = max;
if (option_debug && iaxdebug)
ast_log(LOG_DEBUG, "New max trunk callno is %d\n", max);
}
static void iax2_destroy_helper(struct chan_iax2_pvt *pvt)
{
if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) {
struct iax2_user *user;
ast_mutex_lock(&userl.lock);
user = userl.users;
while (user) {
if (!strcmp(user->name, pvt->username)) {
user->curauthreq--;
break;
}
user = user->next;
}
ast_mutex_unlock(&userl.lock);
}
/* No more pings or lagrq's */
if (pvt->pingid > -1)
ast_sched_del(sched, pvt->pingid);
if (pvt->lagid > -1)
ast_sched_del(sched, pvt->lagid);
if (pvt->autoid > -1)
ast_sched_del(sched, pvt->autoid);
if (pvt->authid > -1)
ast_sched_del(sched, pvt->authid);
if (pvt->initid > -1)
ast_sched_del(sched, pvt->initid);
#ifdef NEWJB
if (pvt->jbid > -1)
ast_sched_del(sched, pvt->jbid);
pvt->jbid = -1;
#endif
pvt->pingid = -1;
pvt->lagid = -1;
pvt->autoid = -1;
pvt->authid = -1;
pvt->initid = -1;
}
static void store_by_peercallno(struct chan_iax2_pvt *pvt)
{
if (!pvt->peercallno) {
ast_log(LOG_ERROR, "This should not be called without a peer call number.\n");
return;
}
ao2_link(iax_peercallno_pvts, pvt);
}
static void remove_by_peercallno(struct chan_iax2_pvt *pvt)
{
if (!pvt->peercallno) {
ast_log(LOG_ERROR, "This should not be called without a peer call number.\n");
return;
}
ao2_unlink(iax_peercallno_pvts, pvt);
}
static void iax2_destroy(int callno)
{
struct chan_iax2_pvt *pvt;
struct ast_channel *owner;
retry:
ast_mutex_lock(&iaxsl[callno]);
pvt = iaxs[callno];
gettimeofday(&lastused[callno], NULL);
if (pvt)
owner = pvt->owner;
else
owner = NULL;
if (owner) {
if (ast_mutex_trylock(&owner->lock)) {
ast_log(LOG_NOTICE, "Avoiding IAX destroy deadlock\n");
ast_mutex_unlock(&iaxsl[callno]);
usleep(1);
goto retry;
}
}
if (!owner)
iaxs[callno] = NULL;
if (pvt) {
if (owner) {
/* If there's an owner, prod it to give up */
owner->_softhangup |= AST_SOFTHANGUP_DEV;
ast_queue_hangup(owner);
} else {
pvt->owner = NULL;
}
if (pvt->peercallno) {
remove_by_peercallno(pvt);
}
if (!owner) {
ao2_ref(pvt, -1);
pvt = NULL;
}
}
if (owner) {
ast_mutex_unlock(&owner->lock);
}
ast_mutex_unlock(&iaxsl[callno]);
if (callno & 0x4000) {
update_max_trunk();
}
}
static void iax2_frame_free(struct iax_frame *fr)
{
if (fr->retrans > -1)
ast_sched_del(sched, fr->retrans);
iax_frame_free(fr);
}
static void pvt_destructor(void *obj)
{
struct chan_iax2_pvt *pvt = obj;
struct iax_frame *cur;
iax2_destroy_helper(pvt);
if (pvt->bridgetrans)
ast_translator_free_path(pvt->bridgetrans);
pvt->bridgetrans = NULL;
/* Already gone */
ast_set_flag(pvt, IAX_ALREADYGONE);
for (cur = iaxq.head; cur ; cur = cur->next) {
/* Cancel any pending transmissions */
if (cur->callno == pvt->callno)
cur->retries = -1;
}
if (pvt->reg) {
pvt->reg->callno = 0;
}
if (!pvt->owner) {
if (pvt->vars) {
ast_variables_destroy(pvt->vars);
pvt->vars = NULL;
}
#ifdef NEWJB
{
jb_frame frame;
while (jb_getall(pvt->jb,&frame) == JB_OK)
iax2_frame_free(frame.data);
jb_destroy(pvt->jb);
}
#endif
}
}
static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, int lockpeer, const char *host)
{
struct chan_iax2_pvt *tmp;
tmp = ao2_alloc(sizeof(*tmp), pvt_destructor);
tmp = malloc(sizeof(struct chan_iax2_pvt));
if (tmp) {
memset(tmp, 0, sizeof(struct chan_iax2_pvt));
tmp->prefs = prefs;
tmp->callno = 0;
tmp->peercallno = 0;
tmp->transfercallno = 0;
tmp->bridgecallno = 0;
tmp->pingid = -1;
tmp->lagid = -1;
tmp->autoid = -1;
@@ -1175,13 +967,13 @@ static struct iax_frame *iaxfrdup2(struct iax_frame *fr)
#define NEW_ALLOW 1
#define NEW_FORCE 2
static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short dcallno, struct chan_iax2_pvt *cur, int check_dcallno)
static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short dcallno, struct chan_iax2_pvt *cur, int full_frame)
{
if ((cur->addr.sin_addr.s_addr == sin->sin_addr.s_addr) &&
(cur->addr.sin_port == sin->sin_port)) {
/* This is the main host */
if ( (cur->peercallno == 0 || cur->peercallno == callno) &&
(check_dcallno ? dcallno == cur->callno : 1) ) {
(full_frame ? dcallno == cur->callno : 1) ) {
/* That's us. Be sure we keep track of the peer call number */
return 1;
}
@@ -1195,6 +987,20 @@ static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short
return 0;
}
static void update_max_trunk(void)
{
int max = TRUNK_CALL_START;
int x;
/* XXX Prolly don't need locks here XXX */
for (x=TRUNK_CALL_START;x<IAX_MAX_CALLS - 1; x++) {
if (iaxs[x])
max = x + 1;
}
maxtrunkcall = max;
if (option_debug && iaxdebug)
ast_log(LOG_DEBUG, "New max trunk callno is %d\n", max);
}
static void update_max_nontrunk(void)
{
int max = 1;
@@ -1256,7 +1062,7 @@ static int make_trunk(unsigned short callno, int locked)
return res;
}
static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int lockpeer, int sockfd, int check_dcallno)
static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int lockpeer, int sockfd, int full_frame)
{
int res = 0;
int x;
@@ -1264,31 +1070,12 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc
char iabuf[INET_ADDRSTRLEN];
char host[80];
if (new <= NEW_ALLOW) {
if (callno) {
struct chan_iax2_pvt *pvt;
struct chan_iax2_pvt tmp_pvt = {
.callno = dcallno,
.peercallno = callno,
/* hack!! */
.frames_received = check_dcallno,
};
memcpy(&tmp_pvt.addr, sin, sizeof(tmp_pvt.addr));
if ((pvt = ao2_find(iax_peercallno_pvts, &tmp_pvt, OBJ_POINTER))) {
res = pvt->callno;
ao2_ref(pvt, -1);
pvt = NULL;
return res;
}
}
/* Look for an existing connection first */
for (x=2;(res < 1) && (x<maxnontrunkcall);x++) {
for (x=1;(res < 1) && (x<maxnontrunkcall);x++) {
ast_mutex_lock(&iaxsl[x]);
if (iaxs[x]) {
/* Look for an exact match */
if (match(sin, callno, dcallno, iaxs[x], check_dcallno)) {
if (match(sin, callno, dcallno, iaxs[x], full_frame)) {
res = x;
}
}
@@ -1298,7 +1085,7 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc
ast_mutex_lock(&iaxsl[x]);
if (iaxs[x]) {
/* Look for an exact match */
if (match(sin, callno, dcallno, iaxs[x], check_dcallno)) {
if (match(sin, callno, dcallno, iaxs[x], full_frame)) {
res = x;
}
}
@@ -1319,10 +1106,10 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc
gettimeofday(&now, NULL);
start = 2 + (rand() % (TRUNK_CALL_START - 1));
start = 1 + (rand() % (TRUNK_CALL_START - 1));
for (x = start; 1; x++) {
if (x == TRUNK_CALL_START) {
x = 1;
x = 0;
continue;
}
@@ -1361,9 +1148,6 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc
iaxs[x]->amaflags = amaflags;
ast_copy_flags(iaxs[x], (&globalflags), IAX_NOTRANSFER | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
ast_copy_string(iaxs[x]->accountcode, accountcode, sizeof(iaxs[x]->accountcode));
if (iaxs[x]->peercallno) {
store_by_peercallno(iaxs[x]);
}
} else {
ast_log(LOG_WARNING, "Out of resources\n");
ast_mutex_unlock(&iaxsl[x]);
@@ -1375,6 +1159,13 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc
return res;
}
static void iax2_frame_free(struct iax_frame *fr)
{
if (fr->retrans > -1)
ast_sched_del(sched, fr->retrans);
iax_frame_free(fr);
}
static int iax2_queue_frame(int callno, struct ast_frame *f)
{
/* Assumes lock for callno is already held... */
@@ -1848,7 +1639,112 @@ static int iax2_predestroy_nolock(int callno)
return res;
}
static void iax2_destroy(int callno)
{
struct chan_iax2_pvt *pvt;
struct iax_frame *cur;
struct ast_channel *owner;
struct iax2_user *user;
retry:
ast_mutex_lock(&iaxsl[callno]);
pvt = iaxs[callno];
gettimeofday(&lastused[callno], NULL);
if (pvt)
owner = pvt->owner;
else
owner = NULL;
if (owner) {
if (ast_mutex_trylock(&owner->lock)) {
ast_log(LOG_NOTICE, "Avoiding IAX destroy deadlock\n");
ast_mutex_unlock(&iaxsl[callno]);
usleep(1);
goto retry;
}
}
if (!owner)
iaxs[callno] = NULL;
if (pvt) {
if (!owner)
pvt->owner = NULL;
if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) {
ast_mutex_lock(&userl.lock);
user = userl.users;
while (user) {
if (!strcmp(user->name, pvt->username)) {
user->curauthreq--;
break;
}
user = user->next;
}
ast_mutex_unlock(&userl.lock);
}
/* No more pings or lagrq's */
if (pvt->pingid > -1)
ast_sched_del(sched, pvt->pingid);
if (pvt->lagid > -1)
ast_sched_del(sched, pvt->lagid);
if (pvt->autoid > -1)
ast_sched_del(sched, pvt->autoid);
if (pvt->authid > -1)
ast_sched_del(sched, pvt->authid);
if (pvt->initid > -1)
ast_sched_del(sched, pvt->initid);
#ifdef NEWJB
if (pvt->jbid > -1)
ast_sched_del(sched, pvt->jbid);
pvt->jbid = -1;
#endif
pvt->pingid = -1;
pvt->lagid = -1;
pvt->autoid = -1;
pvt->authid = -1;
pvt->initid = -1;
if (pvt->bridgetrans)
ast_translator_free_path(pvt->bridgetrans);
pvt->bridgetrans = NULL;
/* Already gone */
ast_set_flag(pvt, IAX_ALREADYGONE);
if (owner) {
/* If there's an owner, prod it to give up */
owner->_softhangup |= AST_SOFTHANGUP_DEV;
ast_queue_hangup(owner);
}
for (cur = iaxq.head; cur ; cur = cur->next) {
/* Cancel any pending transmissions */
if (cur->callno == pvt->callno)
cur->retries = -1;
}
if (pvt->reg) {
pvt->reg->callno = 0;
}
if (!owner) {
if (pvt->vars) {
ast_variables_destroy(pvt->vars);
pvt->vars = NULL;
}
#ifdef NEWJB
{
jb_frame frame;
while(jb_getall(pvt->jb,&frame) == JB_OK)
iax2_frame_free(frame.data);
jb_destroy(pvt->jb);
}
#endif
free(pvt);
}
}
if (owner) {
ast_mutex_unlock(&owner->lock);
}
ast_mutex_unlock(&iaxsl[callno]);
if (callno & 0x4000)
update_max_trunk();
}
static void iax2_destroy_nolock(int callno)
{
/* Actually it's easier to unlock, kill it, and relock */
@@ -2751,7 +2647,7 @@ static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in
if (peername) {
var = ast_load_realtime("iaxpeers", "name", peername, "host", "dynamic", NULL);
if (!var && sin)
var = ast_load_realtime("iaxpeers", "name", peername, "host", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), (char *) NULL);
var = ast_load_realtime("iaxpeers", "name", peername, "host", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr));
} else if (sin) {
char porta[25];
ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr);
@@ -2875,7 +2771,7 @@ static struct iax2_user *realtime_user(const char *username, struct sockaddr_in
var = ast_load_realtime("iaxusers", "name", username, "host", "dynamic", NULL);
if (!var && sin)
var = ast_load_realtime("iaxusers", "name", username, "host", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), (char *) NULL);
var = ast_load_realtime("iaxusers", "name", username, "host", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr));
if (!var && sin) {
char porta[6];
snprintf(porta, sizeof(porta), "%d", ntohs(sin->sin_port));
@@ -3100,15 +2996,6 @@ 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)
{
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) };
return sendto(defaultsockfd, &f, sizeof(f), 0, (struct sockaddr *)sin, sizeof(*sin));
}
/*!
* \brief Parses an IAX dial string into its component parts.
* \param data the string to be parsed
@@ -3588,41 +3475,9 @@ static int iax2_answer(struct ast_channel *c)
static int iax2_indicate(struct ast_channel *c, int condition)
{
unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
struct chan_iax2_pvt *pvt;
int res;
if (option_debug && iaxdebug)
ast_log(LOG_DEBUG, "Indicating condition %d\n", condition);
ast_mutex_lock(&iaxsl[callno]);
pvt = iaxs[callno];
if (!pvt->peercallno) {
/* We don't know the remote side's call number, yet. :( */
int count = 10;
while (count-- && pvt && !pvt->peercallno) {
ast_mutex_unlock(&iaxsl[callno]);
usleep(1);
ast_mutex_lock(&iaxsl[callno]);
pvt = iaxs[callno];
}
if (!pvt->peercallno) {
/* Even after waiting, we still haven't completed a handshake with
* our peer, so we can't send this frame. Bail out. */
res = -1;
goto done;
}
}
res = send_command(iaxs[callno], AST_FRAME_CONTROL, condition, 0, NULL, 0, -1);
done:
ast_mutex_unlock(&iaxsl[callno]);
return res;
return send_command_locked(callno, AST_FRAME_CONTROL, condition, 0, NULL, 0, -1);
}
static int iax2_transfer(struct ast_channel *c, const char *dest)
@@ -5723,13 +5578,7 @@ static int complete_transfer(int callno, struct iax_ies *ies)
pvt->rseqno = 0;
pvt->iseqno = 0;
pvt->aseqno = 0;
if (pvt->peercallno) {
remove_by_peercallno(pvt);
}
pvt->peercallno = peercallno;
store_by_peercallno(pvt);
pvt->transferring = TRANSFER_NONE;
pvt->svoiceformat = -1;
pvt->voiceformat = 0;
@@ -6088,34 +5937,23 @@ static int registry_authrequest(char *name, int callno)
{
struct iax_ie_data ied;
struct iax2_peer *p;
int authmethods;
if (!iaxs[callno]) {
return 0;
}
/* SLD: third call to find_peer in registration */
if ((p = find_peer(name, 1))) {
lastauthmethod = p->authmethods;
}
authmethods = p ? p->authmethods : lastauthmethod ? lastauthmethod : (IAX_AUTH_PLAINTEXT | IAX_AUTH_MD5);
if (p && ast_test_flag(p, IAX_TEMPONLY)) {
destroy_peer(p);
} else if (!delayreject) {
ast_log(LOG_WARNING, "No such peer '%s'\n", name);
return 0;
}
memset(&ied, 0, sizeof(ied));
iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods);
if (authmethods & (IAX_AUTH_RSA | IAX_AUTH_MD5)) {
/* Build the challenge */
snprintf(iaxs[callno]->challenge, sizeof(iaxs[callno]->challenge), "%d", rand());
iax_ie_append_str(&ied, IAX_IE_CHALLENGE, iaxs[callno]->challenge);
}
iax_ie_append_str(&ied, IAX_IE_USERNAME, name);
return send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1);;
p = find_peer(name, 1);
if (p) {
memset(&ied, 0, sizeof(ied));
iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods);
if (p->authmethods & (IAX_AUTH_RSA | IAX_AUTH_MD5)) {
/* Build the challenge */
snprintf(iaxs[callno]->challenge, sizeof(iaxs[callno]->challenge), "%d", rand());
iax_ie_append_str(&ied, IAX_IE_CHALLENGE, iaxs[callno]->challenge);
}
iax_ie_append_str(&ied, IAX_IE_USERNAME, name);
if (ast_test_flag(p, IAX_TEMPONLY))
destroy_peer(p);
return send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1);;
}
ast_log(LOG_WARNING, "No such peer '%s'\n", name);
return 0;
}
static int registry_rerequest(struct iax_ies *ies, int callno, struct sockaddr_in *sin)
@@ -6850,17 +6688,6 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
} else {
f.subclass = uncompress_subclass(fh->csub);
}
/* 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);
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)))
@@ -6871,27 +6698,8 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
f.subclass = 0;
}
if (!fr->callno) {
int check_dcallno = 0;
/*
* We enforce accurate destination call numbers for all full frames except
* LAGRQ and PING commands. This is because older versions of Asterisk
* schedule these commands to get sent very quickly, and they will sometimes
* be sent before they receive the first frame from the other side. When
* that happens, it doesn't contain the destination call number. However,
* not checking it for these frames is safe.
*
* Discussed in the following thread:
* http://lists.digium.com/pipermail/asterisk-dev/2008-May/033217.html
*/
if (ntohs(mh->callno) & IAX_FLAG_FULL) {
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)
fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, 1, fd, ntohs(mh->callno) & IAX_FLAG_FULL);
if (fr->callno > 0)
ast_mutex_lock(&iaxsl[fr->callno]);
@@ -6930,18 +6738,8 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
if (!inaddrcmp(&sin, &iaxs[fr->callno]->addr) && !minivid &&
f.subclass != IAX_COMMAND_TXCNT && /* for attended transfer */
f.subclass != IAX_COMMAND_TXACC) { /* for attended transfer */
unsigned short new_peercallno;
new_peercallno = (unsigned short) (ntohs(mh->callno) & ~IAX_FLAG_FULL);
if (new_peercallno && new_peercallno != iaxs[fr->callno]->peercallno) {
if (iaxs[fr->callno]->peercallno) {
remove_by_peercallno(iaxs[fr->callno]);
}
iaxs[fr->callno]->peercallno = new_peercallno;
store_by_peercallno(iaxs[fr->callno]);
}
}
f.subclass != IAX_COMMAND_TXACC) /* for attended transfer */
iaxs[fr->callno]->peercallno = (unsigned short)(ntohs(mh->callno) & ~IAX_FLAG_FULL);
if (ntohs(mh->callno) & IAX_FLAG_FULL) {
if (option_debug && iaxdebug)
ast_log(LOG_DEBUG, "Received packet %d, (%d, %d)\n", fh->oseqno, f.frametype, f.subclass);
@@ -7954,10 +7752,6 @@ retryowner2:
break;
case IAX_COMMAND_FWDOWNL:
/* Firmware download */
if (!ast_test_flag(&globalflags, IAX_ALLOWFWDOWNLOAD)) {
send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_UNSUPPORT, 0, NULL, 0, -1);
break;
}
memset(&ied0, 0, sizeof(ied0));
res = iax_firmware_append(&ied0, (unsigned char *)ies.devicetype, ies.fwdesc);
if (res < 0)
@@ -9225,8 +9019,6 @@ static int set_config(char *config_file, int reload)
delayreject = ast_true(v->value);
else if (!strcasecmp(v->name, "mailboxdetail"))
ast_set2_flag((&globalflags), ast_true(v->value), IAX_MESSAGEDETAIL);
else if (!strcasecmp(v->name, "allowfwdownload"))
ast_set2_flag((&globalflags), ast_true(v->value), IAX_ALLOWFWDOWNLOAD);
else if (!strcasecmp(v->name, "rtcachefriends"))
ast_set2_flag((&globalflags), ast_true(v->value), IAX_RTCACHEFRIENDS);
else if (!strcasecmp(v->name, "rtignoreregexpire"))
@@ -10066,8 +9858,6 @@ static int __unload_module(void)
for (x = 0; x < IAX_MAX_CALLS; x++)
ast_mutex_destroy(&iaxsl[x]);
ao2_ref(iax_peercallno_pvts, -1);
return 0;
}
@@ -10077,23 +9867,6 @@ int unload_module()
return __unload_module();
}
static int pvt_hash_cb(const void *obj, const int flags)
{
const struct chan_iax2_pvt *pvt = obj;
return pvt->peercallno;
}
static int pvt_cmp_cb(void *obj, void *arg, int flags)
{
struct chan_iax2_pvt *pvt = obj, *pvt2 = arg;
/* The frames_received field is used to hold whether we're matching
* against a full frame or not ... */
return match(&pvt2->addr, pvt2->peercallno, pvt2->callno, pvt,
pvt2->frames_received) ? CMP_MATCH : 0;
}
/*--- load_module: Load IAX2 module, load configuraiton ---*/
int load_module(void)
@@ -10182,11 +9955,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);

View File

@@ -6831,14 +6831,12 @@ static int get_destination(struct sip_pvt *p, struct sip_request *oreq)
return -1;
}
from += 4;
if (pedanticsipchecking) {
ast_uri_decode(from);
}
} else
from = NULL;
if (pedanticsipchecking) {
ast_uri_decode(uri);
ast_uri_decode(from);
}
/* Get the target domain first and user */
@@ -7263,10 +7261,11 @@ static int check_user_full(struct sip_pvt *p, struct sip_request *req, int sipme
while(*t && (*t > 32) && (*t != ';'))
t++;
*t = '\0';
ast_copy_string(from, get_header(req, "From"), sizeof(from));
of = get_header(req, "From");
if (pedanticsipchecking)
ast_uri_decode(from);
ast_uri_decode(of);
ast_copy_string(from, of, sizeof(from));
memset(calleridname,0,sizeof(calleridname));
get_calleridname(from, calleridname, sizeof(calleridname));

View File

@@ -255,16 +255,6 @@ autokill=yes
; The default value is 'host'
;
;codecpriority=host
;
; allowfwdownload controls whether this host will serve out firmware to
; IAX clients which request it. This has only been used for the IAXy,
; and it has been recently proven that this firmware distribution method
; can be used as a source of traffic amplification attacks. Also, the
; IAXy firmware has not been updated for at least 18 months, so unless
; you are provisioning IAXys in a secure network, we recommend that you
; leave this option to the default, off.
;
;allowfwdownload=yes
;rtcachefriends=yes ; Cache realtime friends by adding them to the internal list
; just like friends added from the config file only on a

View File

@@ -1,14 +1,5 @@
#!/bin/sh -e
if [ -f codecs/ilbc/iLBC_define.h ]; then
echo "***"
echo "The iLBC source code appears to already be present and does not"
echo "need to be downloaded."
echo "***"
exit 1
fi
echo "***"
echo "This script will download the Global IP Solutions iLBC encoder/decoder"
echo "source code from http://ilbcfreeware.org. Use of this code requires"
@@ -18,7 +9,7 @@ echo "This script assumes that you have already agreed to the license agreement.
echo "If you have not done so, you can abort the script now."
echo "***"
read tmp
read
wget -P codecs/ilbc http://www.ietf.org/rfc/rfc3951.txt
@@ -29,5 +20,3 @@ wget -P codecs/ilbc http://www.ilbcfreeware.org/documentation/extract-cfile.awk
echo "***"
echo "The iLBC source code download is complete."
echo "***"
exit 0

View File

@@ -1,543 +0,0 @@
/*
* astobj2 - replacement containers for asterisk data structures.
*
* Copyright (C) 2006 Marta Carbone, Luigi Rizzo - Univ. di Pisa, Italy
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#ifndef _ASTERISK_ASTOBJ2_H
#define _ASTERISK_ASTOBJ2_H
#include "asterisk/compat.h"
/*! \file
*
* \brief Object Model implementing objects and containers.
These functions implement an abstraction for objects (with
locks and reference counts) and containers for these user-defined objects,
supporting locking, reference counting and callbacks.
The internal implementation of the container is opaque to the user,
so we can use different data structures as needs arise.
At the moment, however, the only internal data structure is a hash
table. When other structures will be implemented, the initialization
function may change.
USAGE - OBJECTS
An object is a block of memory that must be allocated with the
function ao2_alloc(), and for which the system keeps track (with
abit of help from the programmer) of the number of references around.
When an object has no more references, it is destroyed, by first
invoking whatever 'destructor' function the programmer specifies
(it can be NULL), and then freeing the memory.
This way objects can be shared without worrying who is in charge
of freeing them.
Basically, creating an object requires the size of the object and
and a pointer to the destructor function:
struct foo *o;
o = ao2_alloc(sizeof(struct foo), my_destructor_fn);
The object returned has a refcount = 1.
Note that the memory for the object is allocated and zeroed.
- We cannot realloc() the object itself.
- We cannot call free(o) to dispose of the object; rather we
tell the system that we do not need the reference anymore:
ao2_ref(o, -1)
causing the destructor to be called (and then memory freed) when
the refcount goes to 0. This is also available as ao2_unref(o),
and returns NULL as a convenience, so you can do things like
o = ao2_unref(o);
and clean the original pointer to prevent errors.
- ao2_ref(o, +1) can be used to modify the refcount on the
object in case we want to pass it around.
- other calls on the object are ao2_lock(obj), ao2_unlock(),
ao2_trylock(), to manipulate the lock.
USAGE - CONTAINERS
A containers is an abstract data structure where we can store
objects, search them (hopefully in an efficient way), and iterate
or apply a callback function to them. A container is just an object
itself.
A container must first be allocated, specifying the initial
parameters. At the moment, this is done as follows:
<b>Sample Usage:</b>
\code
struct ao2_container *c;
c = ao2_container_alloc(MAX_BUCKETS, my_hash_fn, my_cmp_fn);
where
- MAX_BUCKETS is the number of buckets in the hash table,
- my_hash_fn() is the (user-supplied) function that returns a
hash key for the object (further reduced moduly MAX_BUCKETS
by the container's code);
- my_cmp_fn() is the default comparison function used when doing
searches on the container,
A container knows little or nothing about the object itself,
other than the fact that it has been created by ao2_alloc()
All knowledge of the (user-defined) internals of the object
is left to the (user-supplied) functions passed as arguments
to ao2_container_alloc().
If we want to insert the object in the container, we should
initialize its fields -- especially, those used by my_hash_fn() --
to compute the bucket to use.
Once done, we can link an object to a container with
ao2_link(c, o);
The function returns NULL in case of errors (and the object
is not inserted in the container). Other values mean success
(we are not supposed to use the value as a pointer to anything).
\note While an object o is in a container, we expect that
my_hash_fn(o) will always return the same value. The function
does not lock the object to be computed, so modifications of
those fields that affect the computation of the hash should
be done by extractiong the object from the container, and
reinserting it after the change (this is not terribly expensive).
\note A container with a single buckets is effectively a linked
list. However there is no ordering among elements.
Objects implement a reference counter keeping the count
of the number of references that reference an object.
When this number becomes zero the destructor will be
called and the object will be free'd.
*/
/*!
* Invoked just before freeing the memory for the object.
* It is passed a pointer to user data.
*/
typedef void (*ao2_destructor_fn)(void *);
void ao2_bt(void); /* backtrace */
/*!
* Allocate and initialize an object.
*
* \param data_size The sizeof() of user-defined structure.
* \param destructor_fn The function destructor (can be NULL)
* \return A pointer to user data.
*
* Allocates a struct astobj2 with sufficient space for the
* user-defined structure.
* \notes:
* - storage is zeroed; XXX maybe we want a flag to enable/disable this.
* - the refcount of the object just created is 1
* - the returned pointer cannot be free()'d or realloc()'ed;
* rather, we just call ao2_ref(o, -1);
*/
void *ao2_alloc(const size_t data_size, ao2_destructor_fn destructor_fn);
/*!
* Reference/unreference an object and return the old refcount.
*
* \param o A pointer to the object
* \param delta Value to add to the reference counter.
* \return The value of the reference counter before the operation.
*
* Increase/decrease the reference counter according
* the value of delta.
*
* If the refcount goes to zero, the object is destroyed.
*
* \note The object must not be locked by the caller of this function, as
* it is invalid to try to unlock it after releasing the reference.
*
* \note if we know the pointer to an object, it is because we
* have a reference count to it, so the only case when the object
* can go away is when we release our reference, and it is
* the last one in existence.
*/
int ao2_ref(void *o, int delta);
/*!
* Lock an object.
*
* \param a A pointer to the object we want lock.
* \return 0 on success, other values on error.
*/
int ao2_lock(void *a);
/*!
* Unlock an object.
*
* \param a A pointer to the object we want unlock.
* \return 0 on success, other values on error.
*/
int ao2_unlock(void *a);
/*!
*
* Containers
containers are data structures meant to store several objects,
and perform various operations on them.
Internally, objects are stored in lists, hash tables or other
data structures depending on the needs.
NOTA BENE: at the moment the only container we support is the
hash table and its degenerate form, the list.
Operations on container include:
c = ao2_container_alloc(size, cmp_fn, hash_fn)
allocate a container with desired size and default compare
and hash function
ao2_find(c, arg, flags)
returns zero or more element matching a given criteria
(specified as arg). Flags indicate how many results we
want (only one or all matching entries), and whether we
should unlink the object from the container.
ao2_callback(c, flags, fn, arg)
apply fn(obj, arg) to all objects in the container.
Similar to find. fn() can tell when to stop, and
do anything with the object including unlinking it.
Note that the entire operation is run with the container
locked, so noone else can change its content while we work on it.
However, we pay this with the fact that doing
anything blocking in the callback keeps the container
blocked.
The mechanism is very flexible because the callback function fn()
can do basically anything e.g. counting, deleting records, etc.
possibly using arg to store the results.
iterate on a container
this is done with the following sequence
struct ao2_container *c = ... // our container
struct ao2_iterator i;
void *o;
i = ao2_iterator_init(c, flags);
while ( (o = ao2_iterator_next(&i)) ) {
... do something on o ...
ao2_ref(o, -1);
}
The difference with the callback is that the control
on how to iterate is left to us.
ao2_ref(c, -1)
dropping a reference to a container destroys it, very simple!
Containers are astobj2 object themselves, and this is why their
implementation is simple too.
*/
/*!
* We can perform different operation on an object. We do this
* according the following flags.
*/
enum search_flags {
/*! unlink the object found */
OBJ_UNLINK = (1 << 0),
/*! on match, don't return the object or increase its reference count. */
OBJ_NODATA = (1 << 1),
/*! don't stop at the first match
* \note This is not fully implemented. */
OBJ_MULTIPLE = (1 << 2),
/*! obj is an object of the same type as the one being searched for.
* This implies that it can be passed to the object's hash function
* for optimized searching. */
OBJ_POINTER = (1 << 3),
};
/*!
* Type of a generic function to generate a hash value from an object.
*
*/
typedef int (*ao2_hash_fn)(const void *obj, const int flags);
/*!
* valid callback results:
* We return a combination of
* CMP_MATCH when the object matches the request,
* and CMP_STOP when we should not continue the search further.
*/
enum _cb_results {
CMP_MATCH = 0x1,
CMP_STOP = 0x2,
};
/*!
* generic function to compare objects.
* This, as other callbacks, should return a combination of
* _cb_results as described above.
*
* \param o object from container
* \param arg search parameters (directly from ao2_find)
* \param flags passed directly from ao2_find
* XXX explain.
*/
/*!
* Type of a generic callback function
* \param obj pointer to the (user-defined part) of an object.
* \param arg callback argument from ao2_callback()
* \param flags flags from ao2_callback()
* The return values are the same as a compare function.
* In fact, they are the same thing.
*/
typedef int (*ao2_callback_fn)(void *obj, void *arg, int flags);
/*!
* Here start declarations of containers.
*/
struct ao2_container;
/*!
* Allocate and initialize a container
* with the desired number of buckets.
*
* We allocate space for a struct astobj_container, struct container
* and the buckets[] array.
*
* \param my_hash_fn Pointer to a function computing a hash value.
* \param my_cmp_fn Pointer to a function comparating key-value
* with a string. (can be NULL)
* \return A pointer to a struct container.
*
* destructor is set implicitly.
*/
struct ao2_container *ao2_container_alloc(const unsigned int n_buckets,
ao2_hash_fn hash_fn, ao2_callback_fn cmp_fn);
/*!
* Returns the number of elements in a container.
*/
int ao2_container_count(struct ao2_container *c);
/*
* Here we have functions to manage objects.
*
* We can use the functions below on any kind of
* object defined by the user.
*/
/*!
* \brief Add an object to a container.
*
* \param c the container to operate on.
* \param newobj the object to be added.
*
* \return NULL on errors, other values on success.
*
* This function inserts an object in a container according its key.
*
* \note Remember to set the key before calling this function.
*
* \note This function automatically increases the reference count to
* account for the reference to the object that the container now holds.
*
* For Asterisk 1.4 only, there is a dirty hack here to ensure that chan_iax2
* can have objects linked in to the container at the head instead of tail
* when it is just a linked list. This is to maintain some existing behavior
* where the order must be maintained as it was before this conversion so that
* matching behavior doesn't change.
*/
#define ao2_link(c, o) __ao2_link(c, o, 0)
void *__ao2_link(struct ao2_container *c, void *newobj, int iax2_hack);
/*!
* \brief Remove an object from the container
*
* \arg c the container
* \arg obj the object to unlink
*
* \retval NULL, always
*
* \note The object requested to be unlinked must be valid. However, if it turns
* out that it is not in the container, this function is still safe to
* be called.
*
* \note If the object gets unlinked from the container, the container's
* reference to the object will be automatically released.
*/
void *ao2_unlink(struct ao2_container *c, void *obj);
/*! \struct Used as return value if the flag OBJ_MULTIPLE is set */
struct ao2_list {
struct ao2_list *next;
void *obj; /* pointer to the user portion of the object */
};
/*!
* ao2_callback() and astob2_find() are the same thing with only one difference:
* the latter uses as a callback the function passed as my_cmp_f() at
* the time of the creation of the container.
*
* \param c A pointer to the container to operate on.
* \param arg passed to the callback.
* \param flags A set of flags specifying the operation to perform,
partially used by the container code, but also passed to
the callback.
* \return A pointer to the object found/marked,
* a pointer to a list of objects matching comparison function,
* NULL if not found.
* If the function returns any objects, their refcount is incremented,
* and the caller is in charge of decrementing them once done.
* Also, in case of multiple values returned, the list used
* to store the objects must be freed by the caller.
*
* This function searches through a container and performs operations
* on objects according on flags passed.
* XXX describe better
* The comparison is done calling the compare function set implicitly.
* The p pointer can be a pointer to an object or to a key,
* we can say this looking at flags value.
* If p points to an object we will search for the object pointed
* by this value, otherwise we serch for a key value.
* If the key is not uniq we only find the first matching valued.
* If we use the OBJ_MARK flags, we mark all the objects matching
* the condition.
*
* The use of flags argument is the follow:
*
* OBJ_UNLINK unlinks the object found
* OBJ_NODATA on match, do return an object
* Callbacks use OBJ_NODATA as a default
* functions such as find() do
* OBJ_MULTIPLE return multiple matches
* Default for _find() is no.
* to a key (not yet supported)
* OBJ_POINTER the pointer is an object pointer
*
* In case we return a list, the callee must take care to destroy
* that list when no longer used.
*
* \note When the returned object is no longer in use, ao2_ref() should
* be used to free the additional reference possibly created by this function.
*/
/* XXX order of arguments to find */
void *ao2_find(struct ao2_container *c, void *arg, enum search_flags flags);
void *ao2_callback(struct ao2_container *c,
enum search_flags flags,
ao2_callback_fn cb_fn, void *arg);
int ao2_match_by_addr(void *user_data, void *arg, int flags);
/*!
*
*
* When we need to walk through a container, we use
* ao2_iterator to keep track of the current position.
*
* Because the navigation is typically done without holding the
* lock on the container across the loop,
* objects can be inserted or deleted or moved
* while we work. As a consequence, there is no guarantee that
* the we manage to touch all the elements on the list, or it
* is possible that we touch the same object multiple times.
* However, within the current hash table container, the following is true:
* - It is not possible to miss an object in the container while iterating
* unless it gets added after the iteration begins and is added to a bucket
* that is before the one the current object is in. In this case, even if
* you locked the container around the entire iteration loop, you still would
* not see this object, because it would still be waiting on the container
* lock so that it can be added.
* - It would be extremely rare to see an object twice. The only way this can
* happen is if an object got unlinked from the container and added again
* during the same iteration. Furthermore, when the object gets added back,
* it has to be in the current or later bucket for it to be seen again.
*
* An iterator must be first initialized with ao2_iterator_init(),
* then we can use o = ao2_iterator_next() to move from one
* element to the next. Remember that the object returned by
* ao2_iterator_next() has its refcount incremented,
* and the reference must be explicitly released when done with it.
*
* Example:
*
* \code
*
* struct ao2_container *c = ... // the container we want to iterate on
* struct ao2_iterator i;
* struct my_obj *o;
*
* i = ao2_iterator_init(c, flags);
*
* while ( (o = ao2_iterator_next(&i)) ) {
* ... do something on o ...
* ao2_ref(o, -1);
* }
*
* \endcode
*
*/
/*!
* You are not supposed to know the internals of an iterator!
* We would like the iterator to be opaque, unfortunately
* its size needs to be known if we want to store it around
* without too much trouble.
* Anyways...
* The iterator has a pointer to the container, and a flags
* field specifying various things e.g. whether the container
* should be locked or not while navigating on it.
* The iterator "points" to the current object, which is identified
* by three values:
* - a bucket number;
* - the object_id, which is also the container version number
* when the object was inserted. This identifies the object
* univoquely, however reaching the desired object requires
* scanning a list.
* - a pointer, and a container version when we saved the pointer.
* If the container has not changed its version number, then we
* can safely follow the pointer to reach the object in constant time.
* Details are in the implementation of ao2_iterator_next()
* A freshly-initialized iterator has bucket=0, version = 0.
*/
struct ao2_iterator {
/*! the container */
struct ao2_container *c;
/*! operation flags */
int flags;
#define F_AO2I_DONTLOCK 1 /*!< don't lock when iterating */
/*! current bucket */
int bucket;
/*! container version */
unsigned int c_version;
/*! pointer to the current object */
void *obj;
/*! container version when the object was created */
unsigned int version;
};
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags);
void *ao2_iterator_next(struct ao2_iterator *a);
#endif /* _ASTERISK_ASTOBJ2_H */

View File

@@ -54,7 +54,6 @@
typedef unsigned char u_int8_t;
typedef unsigned short u_int16_t;
typedef unsigned int u_int32_t;
typedef unsigned int uint;
#endif
char* strsep(char** str, const char* delims);