summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/asterisk/rtp_engine.h6
-rw-r--r--include/asterisk/stringfields.h295
-rw-r--r--main/rtp_engine.c29
-rw-r--r--main/sdp_srtp.c2
-rw-r--r--main/stringfields.c508
-rw-r--r--main/utils.c371
-rw-r--r--res/res_http_websocket.c2
-rw-r--r--res/res_rtp_asterisk.c27
-rw-r--r--tests/test_stringfields.c205
9 files changed, 951 insertions, 494 deletions
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index f9a568520..b7be5e8f4 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -2199,20 +2199,22 @@ int ast_rtp_instance_sendcng(struct ast_rtp_instance *instance, int level);
* \param instance the RTP instance
* \param remote_policy the remote endpoint's policy
* \param local_policy our policy for this RTP instance's remote endpoint
+ * \param rtcp 1 for dedicated RTCP policies
*
* \retval 0 Success
* \retval non-zero Failure
*/
-int ast_rtp_instance_add_srtp_policy(struct ast_rtp_instance *instance, struct ast_srtp_policy* remote_policy, struct ast_srtp_policy *local_policy);
+int ast_rtp_instance_add_srtp_policy(struct ast_rtp_instance *instance, struct ast_srtp_policy* remote_policy, struct ast_srtp_policy *local_policy, int rtcp);
/*!
* \brief Obtain the SRTP instance associated with an RTP instance
*
* \param instance the RTP instance
+ * \param rtcp 1 to request instance for RTCP
* \retval the SRTP instance on success
* \retval NULL if no SRTP instance exists
*/
-struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance);
+struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance, int rtcp);
/*! \brief Custom formats declared in codecs.conf at startup must be communicated to the rtp_engine
* so their mime type can payload number can be initialized. */
diff --git a/include/asterisk/stringfields.h b/include/asterisk/stringfields.h
index d0879b2ba..c24424b0a 100644
--- a/include/asterisk/stringfields.h
+++ b/include/asterisk/stringfields.h
@@ -17,6 +17,7 @@
*/
/*! \file
+ \page Stringfields String Fields
\brief String fields in structures
This file contains objects and macros used to manage string
@@ -93,6 +94,80 @@
ast_free(x);
\endcode
+ A new feature "Extended String Fields" has been added in 13.9.0.
+
+ An extended field is one that is declared outside the AST_DECLARE_STRING_FIELDS
+ block but still inside the parent structure. It's most useful for extending
+ structures where adding a new string field to an existing AST_DECLARE_STRING_FIELDS
+ block would break ABI compatibility.
+
+ Example:
+
+ \code
+ struct original_structure_version {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(foo);
+ AST_STRING_FIELD(bar);
+ );
+ int x1;
+ int x2;
+ };
+ \endcode
+
+ Adding "blah" to the existing string fields breaks ABI compatibility because it changes
+ the offsets of x1 and x2.
+
+ \code
+ struct new_structure_version {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(foo);
+ AST_STRING_FIELD(bar);
+ AST_STRING_FIELD(blah);
+ );
+ int x1;
+ int x2;
+ };
+ \endcode
+
+ However, adding "blah" as an extended string field to the end of the structure doesn't break
+ ABI compatibility but still allows the use of the existing pool.
+
+ \code
+ struct new_structure_version {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(foo);
+ AST_STRING_FIELD(bar);
+ );
+ int x1;
+ int x2;
+ AST_STRING_FIELD_EXTENDED(blah);
+ };
+ \endcode
+
+ The only additional step required is to call ast_string_field_init_extended so the
+ pool knows about the new field. It must be called AFTER ast_string_field_init or
+ ast_calloc_with_stringfields. Although ast_calloc_with_stringfields is used in the
+ sample below, it's not necessary for extended string fields.
+
+ \code
+
+ struct new_structure_version *x = ast_calloc_with_stringfields(1, struct new_structure_version, 252);
+ if (!x) {
+ return;
+ }
+
+ ast_string_field_init_extended(x, blah);
+ \endcode
+
+ The new field can now be treated just like any other string field and it's storage will
+ be released with the rest of the string fields.
+
+ \code
+ ast_string_field_set(x, foo, "infinite loop");
+ ast_stringfield_free_memory(x);
+ ast_free(x);
+ \endcode
+
This completes the API description.
*/
@@ -100,6 +175,7 @@
#define _ASTERISK_STRINGFIELDS_H
#include "asterisk/inline_api.h"
+#include "asterisk/vector.h"
/*!
\internal
@@ -139,11 +215,28 @@ struct ast_string_field_pool {
/*!
\internal
+ \brief The definition for the string field vector used for compare and copy
+ \since 13.9.0
+*/
+AST_VECTOR(ast_string_field_vector, const char **);
+
+/*!
+ \internal
+ \brief Structure used to hold a pointer to the embedded pool and the field vector
+ \since 13.9.0
+*/
+struct ast_string_field_header {
+ struct ast_string_field_pool *embedded_pool; /*!< pointer to the embedded pool, if any */
+ struct ast_string_field_vector string_fields; /*!< field vector for compare and copy */
+};
+
+/*!
+ \internal
\brief Structure used to manage the storage for a set of string fields.
*/
struct ast_string_field_mgr {
ast_string_field last_alloc; /*!< the last field allocated */
- struct ast_string_field_pool *embedded_pool; /*!< pointer to the embedded pool, if any */
+ struct ast_string_field_header *header; /*!< pointer to the header */
#if defined(__AST_DEBUG_MALLOC)
const char *owner_file; /*!< filename of owner */
const char *owner_func; /*!< function name of owner */
@@ -218,6 +311,29 @@ void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
#define AST_STRING_FIELD(name) const ast_string_field name
/*!
+ \brief Declare an extended string field
+ \since 13.9.0
+
+ \param name The field name
+*/
+#define AST_STRING_FIELD_EXTENDED(name) AST_STRING_FIELD(name)
+
+enum ast_stringfield_cleanup_type {
+ /*!
+ * Reset all string fields and free all extra pools that may have been created
+ * The allocation or structure can be reused as is.
+ */
+ AST_STRINGFIELD_RESET = 0,
+ /*!
+ * Reset all string fields and free all pools.
+ * If the pointer was returned by ast_calloc_with_stringfields, it can NOT be reused
+ * and should be immediately freed. Otherwise, you must call ast_string_field_init
+ * again if you want to reuse it.
+ */
+ AST_STRINGFIELD_DESTROY = -1,
+};
+
+/*!
\brief Declare the fields needed in a structure
\param field_list The list of fields to declare, using AST_STRING_FIELD() for each one.
Internally, string fields are stored as a pointer to the head of the pool,
@@ -239,17 +355,65 @@ void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
\brief Initialize a field pool and fields
\param x Pointer to a structure containing fields
\param size Amount of storage to allocate.
- Use 0 to reset fields to the default value,
+ Use AST_STRINGFIELD_RESET to reset fields to the default value,
and release all but the most recent pool.
- size<0 (used internally) means free all pools.
+ AST_STRINGFIELD_DESTROY (used internally) means free all pools which is
+ equivalent to calling ast_string_field_free_memory.
+
\return 0 on success, non-zero on failure
*/
#define ast_string_field_init(x, size) \
- __ast_string_field_init(&(x)->__field_mgr, &(x)->__field_mgr_pool, size, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL) { \
+ __res__ = __ast_string_field_init(&(x)->__field_mgr, &(x)->__field_mgr_pool, size, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ } \
+ __res__ ; \
+})
+
+/*!
+ * \brief free all memory - to be called before destroying the object
+ *
+ * \param x
+ *
+ */
+#define ast_string_field_free_memory(x) \
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL) { \
+ __res__ = __ast_string_field_free_memory(&(x)->__field_mgr, &(x)->__field_mgr_pool, \
+ AST_STRINGFIELD_DESTROY, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ } \
+ __res__; \
+})
+
+int __ast_string_field_free_memory(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, enum ast_stringfield_cleanup_type cleanup_type,
+ const char *file, int lineno, const char *func);
-/*! \brief free all memory - to be called before destroying the object */
-#define ast_string_field_free_memory(x) \
- __ast_string_field_init(&(x)->__field_mgr, &(x)->__field_mgr_pool, -1, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+/*!
+ * \brief Initialize an extended string field
+ * \since 13.9.0
+ *
+ * \param x Pointer to a structure containing the field
+ * \param field The extended field to initialize
+ * \retval zero on success
+ * \retval non-zero on error
+ *
+ * \note
+ * This macro must be called on ALL fields defined with AST_STRING_FIELD_EXTENDED after
+ * ast_string_field_init has been called.
+ */
+#define ast_string_field_init_extended(x, field) \
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL && (x)->__field_mgr.header != NULL) { \
+ ast_string_field *non_const = (ast_string_field *)&(x)->field; \
+ *non_const = __ast_string_field_empty; \
+ __res__ = AST_VECTOR_APPEND(&(x)->__field_mgr.header->string_fields, non_const); \
+ } \
+ __res__; \
+})
/*!
* \internal
@@ -260,9 +424,10 @@ int __ast_string_field_init(struct ast_string_field_mgr *mgr, struct ast_string_
/*!
* \brief Allocate a structure with embedded stringfields in a single allocation
- * \param n Number of structures to allocate (see ast_calloc)
+ * \param n Current imlementation only allows 1 structure to be allocated
* \param type The type of structure to allocate
* \param size The number of bytes of space (minimum) to allocate for stringfields to use
+ * in each structure
*
* This function will allocate memory for one or more structures that use stringfields, and
* also allocate space for the stringfields and initialize the stringfield management
@@ -271,16 +436,16 @@ int __ast_string_field_init(struct ast_string_field_mgr *mgr, struct ast_string_
* \since 1.8
*/
#define ast_calloc_with_stringfields(n, type, size) \
- __ast_calloc_with_stringfields(n, sizeof(type), offsetof(type, __field_mgr), offsetof(type, __field_mgr_pool), \
- size, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+ __ast_calloc_with_stringfields(n, sizeof(type), offsetof(type, __field_mgr), \
+ offsetof(type, __field_mgr_pool), size, __FILE__, __LINE__, __PRETTY_FUNCTION__)
/*!
* \internal
* \brief internal version of ast_calloc_with_stringfields
*/
-void * attribute_malloc __ast_calloc_with_stringfields(unsigned int num_structs, size_t struct_size, size_t field_mgr_offset,
- size_t field_mgr_pool_offset, size_t pool_size, const char *file,
- int lineno, const char *func);
+void * attribute_malloc __ast_calloc_with_stringfields(unsigned int num_structs,
+ size_t struct_size, size_t field_mgr_offset, size_t field_mgr_pool_offset, size_t pool_size,
+ const char *file, int lineno, const char *func);
/*!
\internal
@@ -314,7 +479,14 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
\retval zero on success
\retval non-zero on error
*/
-#define ast_string_field_ptr_set(x, ptr, data) ast_string_field_ptr_set_by_fields((x)->__field_mgr_pool, (x)->__field_mgr, ptr, data)
+#define ast_string_field_ptr_set(x, ptr, data) \
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL) { \
+ __res__ = ast_string_field_ptr_set_by_fields((x)->__field_mgr_pool, (x)->__field_mgr, ptr, data); \
+ } \
+ __res__; \
+})
#define ast_string_field_ptr_set_by_fields(field_mgr_pool, field_mgr, ptr, data) \
({ \
@@ -348,7 +520,14 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
\retval zero on success
\retval non-zero on error
*/
-#define ast_string_field_set(x, field, data) ast_string_field_ptr_set(x, &(x)->field, data)
+#define ast_string_field_set(x, field, data) \
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL) { \
+ __res__ = ast_string_field_ptr_set(x, &(x)->field, data); \
+ } \
+ __res__; \
+})
/*!
\brief Set a field to a complex (built) value
@@ -359,7 +538,14 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
\return nothing
*/
#define ast_string_field_ptr_build(x, ptr, fmt, args...) \
- __ast_string_field_ptr_build(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) ptr, fmt, args)
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL) { \
+ __ast_string_field_ptr_build(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) ptr, fmt, args); \
+ __res__ = 0; \
+ } \
+ __res__; \
+})
/*!
\brief Set a field to a complex (built) value
@@ -370,7 +556,14 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
\return nothing
*/
#define ast_string_field_build(x, field, fmt, args...) \
- __ast_string_field_ptr_build(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) &(x)->field, fmt, args)
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL) { \
+ __ast_string_field_ptr_build(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) &(x)->field, fmt, args); \
+ __res__ = 0; \
+ } \
+ __res__; \
+})
/*!
\brief Set a field to a complex (built) value with prebuilt va_lists.
@@ -381,7 +574,14 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
\return nothing
*/
#define ast_string_field_ptr_build_va(x, ptr, fmt, args) \
- __ast_string_field_ptr_build_va(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) ptr, fmt, args)
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL) { \
+ __ast_string_field_ptr_build_va(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) ptr, fmt, args); \
+ __res__ = 0; \
+ } \
+ __res__; \
+})
/*!
\brief Set a field to a complex (built) value
@@ -392,7 +592,14 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
\return nothing
*/
#define ast_string_field_build_va(x, field, fmt, args) \
- __ast_string_field_ptr_build_va(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) &(x)->field, fmt, args)
+({ \
+ int __res__ = -1; \
+ if (((void *)(x)) != NULL) { \
+ __ast_string_field_ptr_build_va(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) &(x)->field, fmt, args); \
+ __res__ = 0; \
+ } \
+ __res__; \
+})
/*!
\brief Compare the string fields in two instances of the same structure
@@ -404,25 +611,16 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
*/
#define ast_string_fields_cmp(instance1, instance2) \
({ \
- int __res__ = 0; \
- size_t __ptr_size__ = sizeof(char *); \
- int __len__ = ((void *)&(instance1)->__field_mgr - (void *)&(instance1)->__field_mgr_pool)/__ptr_size__ - 1; \
- int __len2__ = ((void *)&(instance2)->__field_mgr - (void *)&(instance2)->__field_mgr_pool)/__ptr_size__ - 1; \
- if (__len__ == __len2__) { \
- char **__head1__ = (void *)&(instance1)->__field_mgr_pool + __ptr_size__; \
- char **__head2__ = (void *)&(instance2)->__field_mgr_pool + __ptr_size__; \
- for (__len__ -= 1; __len__ >= 0; __len__--) { \
- __res__ = strcmp(__head1__[__len__], __head2__[__len__]); \
- if (__res__) { \
- break; \
- } \
- } \
- } else { \
- __res__ = -1; \
+ int __res__ = -1; \
+ if (((void *)(instance1)) != NULL && ((void *)(instance2)) != NULL) { \
+ __res__ = __ast_string_fields_cmp(&(instance1)->__field_mgr.header->string_fields, \
+ &(instance2)->__field_mgr.header->string_fields); \
} \
__res__; \
})
+int __ast_string_fields_cmp(struct ast_string_field_vector *left, struct ast_string_field_vector *right);
+
/*!
\brief Copy all string fields from one instance to another of the same structure
\since 12
@@ -433,27 +631,16 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
*/
#define ast_string_fields_copy(copy, orig) \
({ \
- int __outer_res__ = 0; \
- size_t __ptr_size__ = sizeof(char *); \
- int __len__ = ((void *)&(copy)->__field_mgr - (void *)&(copy)->__field_mgr_pool)/__ptr_size__ - 1; \
- int __len2__ = ((void *)&(orig)->__field_mgr - (void *)&(orig)->__field_mgr_pool)/__ptr_size__ - 1; \
- if (__len__ == __len2__) { \
- ast_string_field *__copy_head__ = (void *)&(copy)->__field_mgr_pool + __ptr_size__; \
- ast_string_field *__orig_head__ = (void *)&(orig)->__field_mgr_pool + __ptr_size__; \
- for (__len2__ -= 1; __len2__ >= 0; __len2__--) { \
- __ast_string_field_release_active((copy)->__field_mgr_pool, __copy_head__[__len2__]); \
- __copy_head__[__len2__] = __ast_string_field_empty; \
- } \
- for (__len__ -= 1; __len__ >= 0; __len__--) { \
- if (ast_string_field_ptr_set((copy), &__copy_head__[__len__], __orig_head__[__len__])) { \
- __outer_res__ = -1; \
- break; \
- } \
- } \
- } else { \
- __outer_res__ = -1; \
+ int __res__ = -1; \
+ if (((void *)(copy)) != NULL && ((void *)(orig)) != NULL) { \
+ __res__ = __ast_string_fields_copy(((copy)->__field_mgr_pool), \
+ (struct ast_string_field_mgr *)&((copy)->__field_mgr), \
+ (struct ast_string_field_mgr *)&((orig)->__field_mgr)); \
} \
- __outer_res__; \
+ __res__; \
})
+int __ast_string_fields_copy(struct ast_string_field_pool *copy_pool,
+ struct ast_string_field_mgr *copy_mgr, struct ast_string_field_mgr *orig_mgr);
+
#endif /* _ASTERISK_STRINGFIELDS_H */
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index 6ff8f5286..462d4c530 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -188,6 +188,8 @@ struct ast_rtp_instance {
struct ast_rtp_glue *glue;
/*! SRTP info associated with the instance */
struct ast_srtp *srtp;
+ /*! SRTP info dedicated for RTCP associated with the instance */
+ struct ast_srtp *rtcp_srtp;
/*! Channel unique ID */
char channel_uniqueid[AST_MAX_UNIQUEID];
/*! Time of last packet sent */
@@ -364,6 +366,10 @@ static void instance_destructor(void *obj)
res_srtp->destroy(instance->srtp);
}
+ if (instance->rtcp_srtp) {
+ res_srtp->destroy(instance->rtcp_srtp);
+ }
+
ast_rtp_codecs_payloads_destroy(&instance->codecs);
/* Drop our engine reference */
@@ -1568,29 +1574,38 @@ int ast_rtp_engine_srtp_is_registered(void)
return res_srtp && res_srtp_policy;
}
-int ast_rtp_instance_add_srtp_policy(struct ast_rtp_instance *instance, struct ast_srtp_policy *remote_policy, struct ast_srtp_policy *local_policy)
+int ast_rtp_instance_add_srtp_policy(struct ast_rtp_instance *instance, struct ast_srtp_policy *remote_policy, struct ast_srtp_policy *local_policy, int rtcp)
{
int res = 0;
+ struct ast_srtp **srtp;
if (!res_srtp) {
return -1;
}
- if (!instance->srtp) {
- res = res_srtp->create(&instance->srtp, instance, remote_policy);
+
+ srtp = rtcp ? &instance->rtcp_srtp : &instance->srtp;
+
+ if (!*srtp) {
+ res = res_srtp->create(srtp, instance, remote_policy);
} else {
- res = res_srtp->replace(&instance->srtp, instance, remote_policy);
+ res = res_srtp->replace(srtp, instance, remote_policy);
}
if (!res) {
- res = res_srtp->add_stream(instance->srtp, local_policy);
+ res = res_srtp->add_stream(*srtp, local_policy);
}
return res;
}
-struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance)
+struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance, int rtcp)
{
- return instance->srtp;
+ if (rtcp && instance->rtcp_srtp) {
+ return instance->rtcp_srtp;
+ }
+ else {
+ return instance->srtp;
+ }
}
int ast_rtp_instance_sendcng(struct ast_rtp_instance *instance, int level)
diff --git a/main/sdp_srtp.c b/main/sdp_srtp.c
index 2c49fd240..d77d4630d 100644
--- a/main/sdp_srtp.c
+++ b/main/sdp_srtp.c
@@ -183,7 +183,7 @@ static int crypto_activate(struct ast_sdp_crypto *p, int suite_val, unsigned cha
}
/* Add the SRTP policies */
- if (ast_rtp_instance_add_srtp_policy(rtp, remote_policy, local_policy)) {
+ if (ast_rtp_instance_add_srtp_policy(rtp, remote_policy, local_policy, 0)) {
ast_log(LOG_WARNING, "Could not set SRTP policies\n");
goto err;
}
diff --git a/main/stringfields.c b/main/stringfields.c
new file mode 100644
index 000000000..67dd06c9b
--- /dev/null
+++ b/main/stringfields.c
@@ -0,0 +1,508 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief String fields
+ *
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_REGISTER_FILE()
+
+#include "asterisk/stringfields.h"
+#include "asterisk/utils.h"
+
+/* this is a little complex... string fields are stored with their
+ allocated size in the bytes preceding the string; even the
+ constant 'empty' string has to be this way, so the code that
+ checks to see if there is enough room for a new string doesn't
+ have to have any special case checks
+*/
+
+static const struct {
+ ast_string_field_allocation allocation;
+ char string[1];
+} __ast_string_field_empty_buffer;
+
+ast_string_field __ast_string_field_empty = __ast_string_field_empty_buffer.string;
+
+#define ALLOCATOR_OVERHEAD 48
+
+static size_t optimal_alloc_size(size_t size)
+{
+ unsigned int count;
+
+ size += ALLOCATOR_OVERHEAD;
+
+ for (count = 1; size; size >>= 1, count++);
+
+ return (1 << count) - ALLOCATOR_OVERHEAD;
+}
+
+static void *calloc_wrapper(unsigned int num_structs, size_t struct_size,
+ const char *file, int lineno, const char *func)
+{
+#if defined(__AST_DEBUG_MALLOC)
+ return __ast_calloc(num_structs, struct_size, file, lineno, func);
+#else
+ return ast_calloc(num_structs, struct_size);
+#endif
+}
+
+/*! \brief add a new block to the pool.
+ * We can only allocate from the topmost pool, so the
+ * fields in *mgr reflect the size of that only.
+ */
+static int add_string_pool(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
+ size_t size, const char *file, int lineno, const char *func)
+{
+ struct ast_string_field_pool *pool;
+ size_t alloc_size = optimal_alloc_size(sizeof(*pool) + size);
+
+ if (!(pool = calloc_wrapper(1, alloc_size, file, lineno, func))) {
+ return -1;
+ }
+
+ pool->prev = *pool_head;
+ pool->size = alloc_size - sizeof(*pool);
+ *pool_head = pool;
+ mgr->last_alloc = NULL;
+
+ return 0;
+}
+
+static void reset_field(const char **p)
+{
+ *p = __ast_string_field_empty;
+}
+
+/*!
+ * \brief Internal cleanup function
+ * \internal
+ * \param mgr
+ * \param pool_head
+ * \param cleanup_type
+ * 0: Reset all string fields and free all pools except the last or embedded pool.
+ * Keep the internal management structures so the structure can be reused.
+ * -1: Reset all string fields and free all pools except the embedded pool.
+ * Free the internal management structures.
+ * \param file
+ * \param lineno
+ * \param func
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int __ast_string_field_free_memory(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, enum ast_stringfield_cleanup_type cleanup_type,
+ const char *file, int lineno, const char *func)
+{
+ struct ast_string_field_pool *cur = NULL;
+ struct ast_string_field_pool *preserve = NULL;
+
+ if (!mgr->header) {
+ return -1;
+ }
+
+ /* reset all the fields regardless of cleanup type */
+ AST_VECTOR_CALLBACK_VOID(&mgr->header->string_fields, reset_field);
+
+ switch (cleanup_type) {
+ case AST_STRINGFIELD_DESTROY:
+ AST_VECTOR_FREE(&mgr->header->string_fields);
+
+ if (mgr->header->embedded_pool) { /* ALWAYS preserve the embedded pool if there is one */
+ preserve = mgr->header->embedded_pool;
+ preserve->used = preserve->active = 0;
+ }
+
+ ast_free(mgr->header);
+ mgr->header = NULL;
+ break;
+ case AST_STRINGFIELD_RESET:
+ /* Preserve the embedded pool if there is one, otherwise the last pool */
+ if (mgr->header->embedded_pool) {
+ preserve = mgr->header->embedded_pool;
+ } else {
+ if (*pool_head == NULL) {
+ ast_log(LOG_WARNING, "trying to reset empty pool\n");
+ return -1;
+ }
+ preserve = *pool_head;
+ }
+ preserve->used = preserve->active = 0;
+ break;
+ default:
+ return -1;
+ }
+
+ cur = *pool_head;
+ while (cur) {
+ struct ast_string_field_pool *prev = cur->prev;
+
+ if (cur != preserve) {
+ ast_free(cur);
+ }
+ cur = prev;
+ }
+
+ *pool_head = preserve;
+ if (preserve) {
+ preserve->prev = NULL;
+ }
+
+ return 0;
+}
+
+/*!
+ * \brief Internal initialization function
+ * \internal
+ * \param mgr
+ * \param pool_head
+ * \param needed
+ * \param file
+ * \param lineno
+ * \param func
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int __ast_string_field_init(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
+ int needed, const char *file, int lineno, const char *func)
+{
+ const char **p = (const char **) pool_head + 1;
+ size_t initial_vector_size = ((size_t) (((char *)mgr) - ((char *)p))) / sizeof(*p);
+
+ if (needed <= 0) {
+ return __ast_string_field_free_memory(mgr, pool_head, needed, file, lineno, func);
+ }
+
+ mgr->last_alloc = NULL;
+#if defined(__AST_DEBUG_MALLOC)
+ mgr->owner_file = file;
+ mgr->owner_func = func;
+ mgr->owner_line = lineno;
+#endif
+
+ if (!(mgr->header = calloc_wrapper(1, sizeof(*mgr->header), file, lineno, func))) {
+ return -1;
+ }
+
+ if (AST_VECTOR_INIT(&mgr->header->string_fields, initial_vector_size)) {
+ ast_free(mgr->header);
+ mgr->header = NULL;
+ return -1;
+ }
+
+ while ((struct ast_string_field_mgr *) p != mgr) {
+ AST_VECTOR_APPEND(&mgr->header->string_fields, p);
+ *p++ = __ast_string_field_empty;
+ }
+
+ *pool_head = NULL;
+ mgr->header->embedded_pool = NULL;
+ if (add_string_pool(mgr, pool_head, needed, file, lineno, func)) {
+ AST_VECTOR_FREE(&mgr->header->string_fields);
+ ast_free(mgr->header);
+ mgr->header = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, size_t needed)
+{
+ char *result = NULL;
+ size_t space = (*pool_head)->size - (*pool_head)->used;
+ size_t to_alloc;
+
+ /* Make room for ast_string_field_allocation and make it a multiple of that. */
+ to_alloc = ast_make_room_for(needed, ast_string_field_allocation);
+ ast_assert(to_alloc % ast_alignof(ast_string_field_allocation) == 0);
+
+ if (__builtin_expect(to_alloc > space, 0)) {
+ size_t new_size = (*pool_head)->size;
+
+ while (new_size < to_alloc) {
+ new_size *= 2;
+ }
+
+#if defined(__AST_DEBUG_MALLOC)
+ if (add_string_pool(mgr, pool_head, new_size, mgr->owner_file, mgr->owner_line, mgr->owner_func))
+ return NULL;
+#else
+ if (add_string_pool(mgr, pool_head, new_size, __FILE__, __LINE__, __FUNCTION__))
+ return NULL;
+#endif
+ }
+
+ /* pool->base is always aligned (gcc aligned attribute). We ensure that
+ * to_alloc is also a multiple of ast_alignof(ast_string_field_allocation)
+ * causing result to always be aligned as well; which in turn fixes that
+ * AST_STRING_FIELD_ALLOCATION(result) is aligned. */
+ result = (*pool_head)->base + (*pool_head)->used;
+ (*pool_head)->used += to_alloc;
+ (*pool_head)->active += needed;
+ result += ast_alignof(ast_string_field_allocation);
+ AST_STRING_FIELD_ALLOCATION(result) = needed;
+ mgr->last_alloc = result;
+
+ return result;
+}
+
+int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, size_t needed, const ast_string_field *ptr)
+{
+ ssize_t grow = needed - AST_STRING_FIELD_ALLOCATION(*ptr);
+ size_t space = (*pool_head)->size - (*pool_head)->used;
+
+ if (*ptr != mgr->last_alloc) {
+ return 1;
+ }
+
+ if (space < grow) {
+ return 1;
+ }
+
+ (*pool_head)->used += grow;
+ (*pool_head)->active += grow;
+ AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
+
+ return 0;
+}
+
+void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
+ const ast_string_field ptr)
+{
+ struct ast_string_field_pool *pool, *prev;
+
+ if (ptr == __ast_string_field_empty) {
+ return;
+ }
+
+ for (pool = pool_head, prev = NULL; pool; prev = pool, pool = pool->prev) {
+ if ((ptr >= pool->base) && (ptr <= (pool->base + pool->size))) {
+ pool->active -= AST_STRING_FIELD_ALLOCATION(ptr);
+ if (pool->active == 0) {
+ if (prev) {
+ prev->prev = pool->prev;
+ ast_free(pool);
+ } else {
+ pool->used = 0;
+ }
+ }
+ break;
+ }
+ }
+}
+
+void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, ast_string_field *ptr,
+ const char *format, va_list ap)
+{
+ size_t needed;
+ size_t available;
+ size_t space = (*pool_head)->size - (*pool_head)->used;
+ int res;
+ ssize_t grow;
+ char *target;
+ va_list ap2;
+
+ /* if the field already has space allocated, try to reuse it;
+ otherwise, try to use the empty space at the end of the current
+ pool
+ */
+ if (*ptr != __ast_string_field_empty) {
+ target = (char *) *ptr;
+ available = AST_STRING_FIELD_ALLOCATION(*ptr);
+ if (*ptr == mgr->last_alloc) {
+ available += space;
+ }
+ } else {
+ /* pool->used is always a multiple of ast_alignof(ast_string_field_allocation)
+ * so we don't need to re-align anything here.
+ */
+ target = (*pool_head)->base + (*pool_head)->used + ast_alignof(ast_string_field_allocation);
+ if (space > ast_alignof(ast_string_field_allocation)) {
+ available = space - ast_alignof(ast_string_field_allocation);
+ } else {
+ available = 0;
+ }
+ }
+
+ va_copy(ap2, ap);
+ res = vsnprintf(target, available, format, ap2);
+ va_end(ap2);
+
+ if (res < 0) {
+ /* Are we out of memory? */
+ return;
+ }
+ if (res == 0) {
+ __ast_string_field_release_active(*pool_head, *ptr);
+ *ptr = __ast_string_field_empty;
+ return;
+ }
+ needed = (size_t)res + 1; /* NUL byte */
+
+ if (needed > available) {
+ /* the allocation could not be satisfied using the field's current allocation
+ (if it has one), or the space available in the pool (if it does not). allocate
+ space for it, adding a new string pool if necessary.
+ */
+ if (!(target = (char *) __ast_string_field_alloc_space(mgr, pool_head, needed))) {
+ return;
+ }
+ vsprintf(target, format, ap);
+ va_end(ap); /* XXX va_end without va_start? */
+ __ast_string_field_release_active(*pool_head, *ptr);
+ *ptr = target;
+ } else if (*ptr != target) {
+ /* the allocation was satisfied using available space in the pool, but not
+ using the space already allocated to the field
+ */
+ __ast_string_field_release_active(*pool_head, *ptr);
+ mgr->last_alloc = *ptr = target;
+ ast_assert(needed < (ast_string_field_allocation)-1);
+ AST_STRING_FIELD_ALLOCATION(target) = (ast_string_field_allocation)needed;
+ (*pool_head)->used += ast_make_room_for(needed, ast_string_field_allocation);
+ (*pool_head)->active += needed;
+ } else if ((grow = (needed - AST_STRING_FIELD_ALLOCATION(*ptr))) > 0) {
+ /* the allocation was satisfied by using available space in the pool *and*
+ the field was the last allocated field from the pool, so it grew
+ */
+ AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
+ (*pool_head)->used += ast_align_for(grow, ast_string_field_allocation);
+ (*pool_head)->active += grow;
+ }
+}
+
+void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, ast_string_field *ptr, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ __ast_string_field_ptr_build_va(mgr, pool_head, ptr, format, ap);
+ va_end(ap);
+}
+
+void *__ast_calloc_with_stringfields(unsigned int num_structs, size_t struct_size,
+ size_t field_mgr_offset, size_t field_mgr_pool_offset, size_t pool_size, const char *file,
+ int lineno, const char *func)
+{
+ struct ast_string_field_mgr *mgr;
+ struct ast_string_field_pool *pool;
+ struct ast_string_field_pool **pool_head;
+ size_t pool_size_needed = sizeof(*pool) + pool_size;
+ size_t size_to_alloc = optimal_alloc_size(struct_size + pool_size_needed);
+ void *allocation;
+ const char **p;
+ size_t initial_vector_size;
+
+ ast_assert(num_structs == 1);
+
+ if (!(allocation = calloc_wrapper(num_structs, size_to_alloc, file, lineno, func))) {
+ return NULL;
+ }
+
+ mgr = allocation + field_mgr_offset;
+
+ /*
+ * The header is calloced in __ast_string_field_init so it also gets calloced here
+ * so __ast_string_fields_free_memory can always just free mgr->header.
+ */
+ mgr->header = calloc_wrapper(1, sizeof(*mgr->header), file, lineno, func);
+ if (!mgr->header) {
+ ast_free(allocation);
+ return NULL;
+ }
+
+ pool = allocation + struct_size;
+ pool_head = allocation + field_mgr_pool_offset;
+ p = (const char **) pool_head + 1;
+ initial_vector_size = ((size_t) (((char *)mgr) - ((char *)p))) / sizeof(*p);
+
+ if (AST_VECTOR_INIT(&mgr->header->string_fields, initial_vector_size)) {
+ ast_free(mgr->header);
+ ast_free(allocation);
+ return NULL;
+ }
+
+ while ((struct ast_string_field_mgr *) p != mgr) {
+ AST_VECTOR_APPEND(&mgr->header->string_fields, p);
+ *p++ = __ast_string_field_empty;
+ }
+
+ mgr->header->embedded_pool = pool;
+ *pool_head = pool;
+ pool->size = size_to_alloc - struct_size - sizeof(*pool);
+#if defined(__AST_DEBUG_MALLOC)
+ mgr->owner_file = file;
+ mgr->owner_func = func;
+ mgr->owner_line = lineno;
+#endif
+
+ return allocation;
+}
+
+int __ast_string_fields_cmp(struct ast_string_field_vector *left,
+ struct ast_string_field_vector *right)
+{
+ int i;
+ int res = 0;
+
+ ast_assert(AST_VECTOR_SIZE(left) == AST_VECTOR_SIZE(right));
+
+ for (i = 0; i < AST_VECTOR_SIZE(left); i++) {
+ if ((res = strcmp(*AST_VECTOR_GET(left, i), *AST_VECTOR_GET(right, i)))) {
+ return res;
+ }
+ }
+
+ return res;
+}
+
+int __ast_string_fields_copy(struct ast_string_field_pool *copy_pool,
+ struct ast_string_field_mgr *copy_mgr, struct ast_string_field_mgr *orig_mgr)
+{
+ int i;
+ struct ast_string_field_vector *dest = &(copy_mgr->header->string_fields);
+ struct ast_string_field_vector *src = &(orig_mgr->header->string_fields);
+
+ ast_assert(AST_VECTOR_SIZE(dest) == AST_VECTOR_SIZE(src));
+
+ for (i = 0; i < AST_VECTOR_SIZE(dest); i++) {
+ __ast_string_field_release_active(copy_pool, *AST_VECTOR_GET(dest, i));
+ *AST_VECTOR_GET(dest, i) = __ast_string_field_empty;
+ }
+
+ for (i = 0; i < AST_VECTOR_SIZE(dest); i++) {
+ if (ast_string_field_ptr_set_by_fields(copy_pool, *copy_mgr, AST_VECTOR_GET(dest, i),
+ *AST_VECTOR_GET(src, i))) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/main/utils.c b/main/utils.c
index 9a400d9db..95eda1102 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -61,9 +61,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/time.h"
#define AST_API_MODULE /* ensure that inlinable API functions will be built in this module if required */
-#include "asterisk/stringfields.h"
-
-#define AST_API_MODULE /* ensure that inlinable API functions will be built in this module if required */
#include "asterisk/utils.h"
#define AST_API_MODULE
@@ -2028,374 +2025,6 @@ char *ast_to_camel_case_delim(const char *s, const char *delim)
return res;
}
-/*
- * stringfields support routines.
- */
-
-/* this is a little complex... string fields are stored with their
- allocated size in the bytes preceding the string; even the
- constant 'empty' string has to be this way, so the code that
- checks to see if there is enough room for a new string doesn't
- have to have any special case checks
-*/
-
-static const struct {
- ast_string_field_allocation allocation;
- char string[1];
-} __ast_string_field_empty_buffer;
-
-ast_string_field __ast_string_field_empty = __ast_string_field_empty_buffer.string;
-
-#define ALLOCATOR_OVERHEAD 48
-
-static size_t optimal_alloc_size(size_t size)
-{
- unsigned int count;
-
- size += ALLOCATOR_OVERHEAD;
-
- for (count = 1; size; size >>= 1, count++);
-
- return (1 << count) - ALLOCATOR_OVERHEAD;
-}
-
-/*! \brief add a new block to the pool.
- * We can only allocate from the topmost pool, so the
- * fields in *mgr reflect the size of that only.
- */
-static int add_string_pool(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
- size_t size, const char *file, int lineno, const char *func)
-{
- struct ast_string_field_pool *pool;
- size_t alloc_size = optimal_alloc_size(sizeof(*pool) + size);
-
-#if defined(__AST_DEBUG_MALLOC)
- if (!(pool = __ast_calloc(1, alloc_size, file, lineno, func))) {
- return -1;
- }
-#else
- if (!(pool = ast_calloc(1, alloc_size))) {
- return -1;
- }
-#endif
-
- pool->prev = *pool_head;
- pool->size = alloc_size - sizeof(*pool);
- *pool_head = pool;
- mgr->last_alloc = NULL;
-
- return 0;
-}
-
-/*
- * This is an internal API, code should not use it directly.
- * It initializes all fields as empty, then uses 'size' for 3 functions:
- * size > 0 means initialize the pool list with a pool of given size.
- * This must be called right after allocating the object.
- * size = 0 means release all pools except the most recent one.
- * If the first pool was allocated via embedding in another
- * object, that pool will be preserved instead.
- * This is useful to e.g. reset an object to the initial value.
- * size < 0 means release all pools.
- * This must be done before destroying the object.
- */
-int __ast_string_field_init(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
- int needed, const char *file, int lineno, const char *func)
-{
- const char **p = (const char **) pool_head + 1;
- struct ast_string_field_pool *cur = NULL;
- struct ast_string_field_pool *preserve = NULL;
-
- /* clear fields - this is always necessary */
- while ((struct ast_string_field_mgr *) p != mgr) {
- *p++ = __ast_string_field_empty;
- }
-
- mgr->last_alloc = NULL;
-#if defined(__AST_DEBUG_MALLOC)
- mgr->owner_file = file;
- mgr->owner_func = func;
- mgr->owner_line = lineno;
-#endif
- if (needed > 0) { /* allocate the initial pool */
- *pool_head = NULL;
- mgr->embedded_pool = NULL;
- return add_string_pool(mgr, pool_head, needed, file, lineno, func);
- }
-
- /* if there is an embedded pool, we can't actually release *all*
- * pools, we must keep the embedded one. if the caller is about
- * to free the structure that contains the stringfield manager
- * and embedded pool anyway, it will be freed as part of that
- * operation.
- */
- if ((needed < 0) && mgr->embedded_pool) {
- needed = 0;
- }
-
- if (needed < 0) { /* reset all pools */
- cur = *pool_head;
- } else if (mgr->embedded_pool) { /* preserve the embedded pool */
- preserve = mgr->embedded_pool;
- cur = *pool_head;
- } else { /* preserve the last pool */
- if (*pool_head == NULL) {
- ast_log(LOG_WARNING, "trying to reset empty pool\n");
- return -1;
- }
- preserve = *pool_head;
- cur = preserve->prev;
- }
-
- if (preserve) {
- preserve->prev = NULL;
- preserve->used = preserve->active = 0;
- }
-
- while (cur) {
- struct ast_string_field_pool *prev = cur->prev;
-
- if (cur != preserve) {
- ast_free(cur);
- }
- cur = prev;
- }
-
- *pool_head = preserve;
-
- return 0;
-}
-
-ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr,
- struct ast_string_field_pool **pool_head, size_t needed)
-{
- char *result = NULL;
- size_t space = (*pool_head)->size - (*pool_head)->used;
- size_t to_alloc;
-
- /* Make room for ast_string_field_allocation and make it a multiple of that. */
- to_alloc = ast_make_room_for(needed, ast_string_field_allocation);
- ast_assert(to_alloc % ast_alignof(ast_string_field_allocation) == 0);
-
- if (__builtin_expect(to_alloc > space, 0)) {
- size_t new_size = (*pool_head)->size;
-
- while (new_size < to_alloc) {
- new_size *= 2;
- }
-
-#if defined(__AST_DEBUG_MALLOC)
- if (add_string_pool(mgr, pool_head, new_size, mgr->owner_file, mgr->owner_line, mgr->owner_func))
- return NULL;
-#else
- if (add_string_pool(mgr, pool_head, new_size, __FILE__, __LINE__, __FUNCTION__))
- return NULL;
-#endif
- }
-
- /* pool->base is always aligned (gcc aligned attribute). We ensure that
- * to_alloc is also a multiple of ast_alignof(ast_string_field_allocation)
- * causing result to always be aligned as well; which in turn fixes that
- * AST_STRING_FIELD_ALLOCATION(result) is aligned. */
- result = (*pool_head)->base + (*pool_head)->used;
- (*pool_head)->used += to_alloc;
- (*pool_head)->active += needed;
- result += ast_alignof(ast_string_field_allocation);
- AST_STRING_FIELD_ALLOCATION(result) = needed;
- mgr->last_alloc = result;
-
- return result;
-}
-
-int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr,
- struct ast_string_field_pool **pool_head, size_t needed,
- const ast_string_field *ptr)
-{
- ssize_t grow = needed - AST_STRING_FIELD_ALLOCATION(*ptr);
- size_t space = (*pool_head)->size - (*pool_head)->used;
-
- if (*ptr != mgr->last_alloc) {
- return 1;
- }
-
- if (space < grow) {
- return 1;
- }
-
- (*pool_head)->used += grow;
- (*pool_head)->active += grow;
- AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
-
- return 0;
-}
-
-void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
- const ast_string_field ptr)
-{
- struct ast_string_field_pool *pool, *prev;
-
- if (ptr == __ast_string_field_empty) {
- return;
- }
-
- for (pool = pool_head, prev = NULL; pool; prev = pool, pool = pool->prev) {
- if ((ptr >= pool->base) && (ptr <= (pool->base + pool->size))) {
- pool->active -= AST_STRING_FIELD_ALLOCATION(ptr);
- if (pool->active == 0) {
- if (prev) {
- prev->prev = pool->prev;
- ast_free(pool);
- } else {
- pool->used = 0;
- }
- }
- break;
- }
- }
-}
-
-void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
- struct ast_string_field_pool **pool_head,
- ast_string_field *ptr, const char *format, va_list ap)
-{
- size_t needed;
- size_t available;
- size_t space = (*pool_head)->size - (*pool_head)->used;
- int res;
- ssize_t grow;
- char *target;
- va_list ap2;
-
- /* if the field already has space allocated, try to reuse it;
- otherwise, try to use the empty space at the end of the current
- pool
- */
- if (*ptr != __ast_string_field_empty) {
- target = (char *) *ptr;
- available = AST_STRING_FIELD_ALLOCATION(*ptr);
- if (*ptr == mgr->last_alloc) {
- available += space;
- }
- } else {
- /* pool->used is always a multiple of ast_alignof(ast_string_field_allocation)
- * so we don't need to re-align anything here.
- */
- target = (*pool_head)->base + (*pool_head)->used + ast_alignof(ast_string_field_allocation);
- if (space > ast_alignof(ast_string_field_allocation)) {
- available = space - ast_alignof(ast_string_field_allocation);
- } else {
- available = 0;
- }
- }
-
- va_copy(ap2, ap);
- res = vsnprintf(target, available, format, ap2);
- va_end(ap2);
-
- if (res < 0) {
- /* Are we out of memory? */
- return;
- }
- if (res == 0) {
- __ast_string_field_release_active(*pool_head, *ptr);
- *ptr = __ast_string_field_empty;
- return;
- }
- needed = (size_t)res + 1; /* NUL byte */
-
- if (needed > available) {
- /* the allocation could not be satisfied using the field's current allocation
- (if it has one), or the space available in the pool (if it does not). allocate
- space for it, adding a new string pool if necessary.
- */
- if (!(target = (char *) __ast_string_field_alloc_space(mgr, pool_head, needed))) {
- return;
- }
- vsprintf(target, format, ap);
- va_end(ap); /* XXX va_end without va_start? */
- __ast_string_field_release_active(*pool_head, *ptr);
- *ptr = target;
- } else if (*ptr != target) {
- /* the allocation was satisfied using available space in the pool, but not
- using the space already allocated to the field
- */
- __ast_string_field_release_active(*pool_head, *ptr);
- mgr->last_alloc = *ptr = target;
- ast_assert(needed < (ast_string_field_allocation)-1);
- AST_STRING_FIELD_ALLOCATION(target) = (ast_string_field_allocation)needed;
- (*pool_head)->used += ast_make_room_for(needed, ast_string_field_allocation);
- (*pool_head)->active += needed;
- } else if ((grow = (needed - AST_STRING_FIELD_ALLOCATION(*ptr))) > 0) {
- /* the allocation was satisfied by using available space in the pool *and*
- the field was the last allocated field from the pool, so it grew
- */
- AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
- (*pool_head)->used += ast_align_for(grow, ast_string_field_allocation);
- (*pool_head)->active += grow;
- }
-}
-
-void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr,
- struct ast_string_field_pool **pool_head,
- ast_string_field *ptr, const char *format, ...)
-{
- va_list ap;
-
- va_start(ap, format);
- __ast_string_field_ptr_build_va(mgr, pool_head, ptr, format, ap);
- va_end(ap);
-}
-
-void *__ast_calloc_with_stringfields(unsigned int num_structs, size_t struct_size, size_t field_mgr_offset,
- size_t field_mgr_pool_offset, size_t pool_size, const char *file,
- int lineno, const char *func)
-{
- struct ast_string_field_mgr *mgr;
- struct ast_string_field_pool *pool;
- struct ast_string_field_pool **pool_head;
- size_t pool_size_needed = sizeof(*pool) + pool_size;
- size_t size_to_alloc = optimal_alloc_size(struct_size + pool_size_needed);
- void *allocation;
- unsigned int x;
-
-#if defined(__AST_DEBUG_MALLOC)
- if (!(allocation = __ast_calloc(num_structs, size_to_alloc, file, lineno, func))) {
- return NULL;
- }
-#else
- if (!(allocation = ast_calloc(num_structs, size_to_alloc))) {
- return NULL;
- }
-#endif
-
- for (x = 0; x < num_structs; x++) {
- void *base = allocation + (size_to_alloc * x);
- const char **p;
-
- mgr = base + field_mgr_offset;
- pool_head = base + field_mgr_pool_offset;
- pool = base + struct_size;
-
- p = (const char **) pool_head + 1;
- while ((struct ast_string_field_mgr *) p != mgr) {
- *p++ = __ast_string_field_empty;
- }
-
- mgr->embedded_pool = pool;
- *pool_head = pool;
- pool->size = size_to_alloc - struct_size - sizeof(*pool);
-#if defined(__AST_DEBUG_MALLOC)
- mgr->owner_file = file;
- mgr->owner_func = func;
- mgr->owner_line = lineno;
-#endif
- }
-
- return allocation;
-}
-
-/* end of stringfields support */
-
AST_MUTEX_DEFINE_STATIC(fetchadd_m); /* used for all fetc&add ops */
int ast_atomic_fetchadd_int_slow(volatile int *p, int v)
diff --git a/res/res_http_websocket.c b/res/res_http_websocket.c
index e16c23c0c..5e0591b13 100644
--- a/res/res_http_websocket.c
+++ b/res/res_http_websocket.c
@@ -24,7 +24,7 @@
*/
/*** MODULEINFO
- <support_level>extended</support_level>
+ <support_level>core</support_level>
***/
#include "asterisk.h"
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 029aff01b..b2ecf5962 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -1982,19 +1982,20 @@ static int dtls_srtp_renegotiate(const void *data)
return 0;
}
-static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct ast_rtp_instance *instance)
+static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct ast_rtp_instance *instance, int rtcp)
{
unsigned char material[SRTP_MASTER_LEN * 2];
unsigned char *local_key, *local_salt, *remote_key, *remote_salt;
struct ast_srtp_policy *local_policy, *remote_policy = NULL;
struct ast_rtp_instance_stats stats = { 0, };
int res = -1;
+ struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;
/* If a fingerprint is present in the SDP make sure that the peer certificate matches it */
if (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_FINGERPRINT) {
X509 *certificate;
- if (!(certificate = SSL_get_peer_certificate(rtp->dtls.ssl))) {
+ if (!(certificate = SSL_get_peer_certificate(dtls->ssl))) {
ast_log(LOG_WARNING, "No certificate was provided by the peer on RTP instance '%p'\n", instance);
return -1;
}
@@ -2028,14 +2029,14 @@ static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct as
}
/* Ensure that certificate verification was successful */
- if ((rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) && SSL_get_verify_result(rtp->dtls.ssl) != X509_V_OK) {
+ if ((rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) && SSL_get_verify_result(dtls->ssl) != X509_V_OK) {
ast_log(LOG_WARNING, "Peer certificate on RTP instance '%p' failed verification test\n",
instance);
return -1;
}
/* Produce key information and set up SRTP */
- if (!SSL_export_keying_material(rtp->dtls.ssl, material, SRTP_MASTER_LEN * 2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) {
+ if (!SSL_export_keying_material(dtls->ssl, material, SRTP_MASTER_LEN * 2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) {
ast_log(LOG_WARNING, "Unable to extract SRTP keying material from DTLS-SRTP negotiation on RTP instance '%p'\n",
instance);
return -1;
@@ -2090,7 +2091,7 @@ static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct as
res_srtp_policy->set_ssrc(remote_policy, 0, 1);
- if (ast_rtp_instance_add_srtp_policy(instance, remote_policy, local_policy)) {
+ if (ast_rtp_instance_add_srtp_policy(instance, remote_policy, local_policy, rtcp)) {
ast_log(LOG_WARNING, "Could not set policies when setting up DTLS-SRTP on '%p'\n", rtp);
goto error;
}
@@ -2121,7 +2122,7 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
{
int len;
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
- struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance);
+ struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance, rtcp);
char *in = buf;
#ifdef HAVE_PJPROJECT
struct ast_sockaddr *loop = rtcp ? &rtp->rtcp_loop : &rtp->rtp_loop;
@@ -2178,10 +2179,8 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
if (SSL_is_init_finished(dtls->ssl)) {
/* Any further connections will be existing since this is now established */
dtls->connection = AST_RTP_DTLS_CONNECTION_EXISTING;
- if (!rtcp) {
- /* Use the keying material to set up key/salt information */
- res = dtls_srtp_setup(rtp, srtp, instance);
- }
+ /* Use the keying material to set up key/salt information */
+ res = dtls_srtp_setup(rtp, srtp, instance, rtcp);
} else {
/* Since we've sent additional traffic start the timeout timer for retransmission */
dtls_srtp_start_timeout_timer(instance, rtp, rtcp);
@@ -2250,7 +2249,7 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
int len = size;
void *temp = buf;
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
- struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance);
+ struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance, rtcp);
int res;
*ice = 0;
@@ -2960,7 +2959,8 @@ static void ast_rtp_update_source(struct ast_rtp_instance *instance)
static void ast_rtp_change_source(struct ast_rtp_instance *instance)
{
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
- struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance);
+ struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance, 0);
+ struct ast_srtp *rtcp_srtp = ast_rtp_instance_get_srtp(instance, 1);
unsigned int ssrc = ast_random();
if (!rtp->lastts) {
@@ -2976,6 +2976,9 @@ static void ast_rtp_change_source(struct ast_rtp_instance *instance)
if (srtp) {
ast_debug(3, "Changing ssrc for SRTP from %u to %u\n", rtp->ssrc, ssrc);
res_srtp->change_source(srtp, rtp->ssrc, ssrc);
+ if (rtcp_srtp != srtp) {
+ res_srtp->change_source(srtp, rtp->ssrc, ssrc);
+ }
}
rtp->ssrc = ssrc;
diff --git a/tests/test_stringfields.c b/tests/test_stringfields.c
index 2eeb83d45..e57f8d7e8 100644
--- a/tests/test_stringfields.c
+++ b/tests/test_stringfields.c
@@ -24,7 +24,6 @@
*
* Test module for string fields API
* \ingroup tests
- * \todo need to test ast_calloc_with_stringfields
*/
/*** MODULEINFO
@@ -50,16 +49,16 @@ AST_TEST_DEFINE(string_field_test)
struct {
AST_DECLARE_STRING_FIELDS (
AST_STRING_FIELD(string1);
- AST_STRING_FIELD(string2);
);
+ AST_STRING_FIELD_EXTENDED(string2);
} test_struct;
struct {
AST_DECLARE_STRING_FIELDS (
AST_STRING_FIELD(string1);
AST_STRING_FIELD(string2);
- AST_STRING_FIELD(string3);
);
+ AST_STRING_FIELD_EXTENDED(string3);
} test_struct2;
switch (cmd) {
@@ -74,6 +73,9 @@ AST_TEST_DEFINE(string_field_test)
break;
}
+ memset(&test_struct, 0, sizeof(test_struct));
+ memset(&test_struct2, 0, sizeof(test_struct));
+
ast_test_status_update(test, "First things first. Let's see if we can actually allocate string fields\n");
if (ast_string_field_init(&test_struct, 32)) {
@@ -82,6 +84,7 @@ AST_TEST_DEFINE(string_field_test)
} else {
ast_test_status_update(test, "All right! Successfully allocated! Now let's get down to business\n");
}
+ ast_string_field_init_extended(&test_struct, string2);
ast_test_status_update(test,"We're going to set some string fields and perform some checks\n");
@@ -255,6 +258,8 @@ AST_TEST_DEFINE(string_field_test)
ast_string_field_init(&test_struct2, 32);
ast_test_status_update(test, "Now using a totally separate area of memory we're going to test a basic pool freeing scenario\n");
+ ast_string_field_init_extended(&test_struct2, string3);
+
ast_string_field_set(&test_struct2, string1, "first");
ast_string_field_set(&test_struct2, string2, "second");
@@ -294,15 +299,22 @@ error:
return AST_TEST_FAIL;
}
+struct test_struct {
+ int foo;
+ AST_DECLARE_STRING_FIELDS (
+ AST_STRING_FIELD(string1);
+ );
+ int foo2;
+ AST_STRING_FIELD_EXTENDED(string2);
+};
+
AST_TEST_DEFINE(string_field_aggregate_test)
{
- struct test_struct {
- AST_DECLARE_STRING_FIELDS (
- AST_STRING_FIELD(string1);
- AST_STRING_FIELD(string2);
- );
- int foo;
- } inst1, inst2, inst3, inst4;
+ enum ast_test_result_state res = AST_TEST_PASS;
+ struct test_struct *inst1 = NULL;
+ struct test_struct *inst2 = NULL;
+ struct test_struct *inst3 = NULL;
+ struct test_struct *inst4 = NULL;
switch (cmd) {
case TEST_INIT:
@@ -316,88 +328,189 @@ AST_TEST_DEFINE(string_field_aggregate_test)
break;
}
- ast_string_field_init(&inst1, 32);
- ast_string_field_init(&inst2, 32);
- ast_string_field_init(&inst3, 32);
- ast_string_field_init(&inst4, 32);
+ inst1 = ast_calloc_with_stringfields(1, struct test_struct, 32);
+ if (!inst1) {
+ ast_test_status_update(test, "Unable to allocate structure 1!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
+ ast_string_field_init_extended(inst1, string2);
+
+ inst2 = ast_calloc_with_stringfields(1, struct test_struct, 32);
+ if (!inst2) {
+ ast_test_status_update(test, "Unable to allocate structure 2!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
+ ast_string_field_init_extended(inst2, string2);
+
+ inst3 = ast_calloc_with_stringfields(1, struct test_struct, 32);
+ if (!inst3) {
+ ast_test_status_update(test, "Unable to allocate structure 3!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
+ ast_string_field_init_extended(inst3, string2);
+
+ inst4 = ast_calloc_with_stringfields(1, struct test_struct, 32);
+ if (!inst4) {
+ ast_test_status_update(test, "Unable to allocate structure 4!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
+ ast_string_field_init_extended(inst4, string2);
+
+
+ ast_string_field_set(inst1, string1, "foo");
+ ast_string_field_set(inst1, string2, "bar");
+ inst1->foo = 1;
+
+ ast_string_field_ptr_set_by_fields(inst2->__field_mgr_pool, inst2->__field_mgr, &inst2->string2, "bar");
+ ast_string_field_ptr_set_by_fields(inst2->__field_mgr_pool, inst2->__field_mgr, &inst2->string1, "foo");
+ inst2->foo = 2;
+
+ if (inst3->__field_mgr.header->embedded_pool->prev) {
+ ast_test_status_update(test, "Structure 3 embedded pool should not have a previous pool!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
+
+ ast_string_field_set(inst3, string1, "foo");
+
+ if (inst3->__field_mgr.header->embedded_pool != inst3->__field_mgr_pool) {
+ ast_test_status_update(test, "Structure 3 embedded pool should have been the current pool!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
+
+ if (inst3->__field_mgr.header->embedded_pool->prev) {
+ ast_test_status_update(test, "Structure 3 embedded pool should not have a previous pool!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
+
+ ast_test_status_update(test, "Structures 3 embedded pool initialized successfully.\n");
+
+ /* Exhaust the embedded pool */
+ ast_string_field_set(inst3, string2, "baz 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890");
+ inst3->foo = 3;
- ast_string_field_set(&inst1, string1, "foo");
- ast_string_field_set(&inst1, string2, "bar");
- inst1.foo = 1;
+ if (inst3->__field_mgr_pool == inst3->__field_mgr.header->embedded_pool) {
+ ast_test_status_update(test, "Structure 3 embedded pool should not have been the current pool!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
- ast_string_field_set(&inst2, string2, "bar");
- ast_string_field_set(&inst2, string1, "foo");
- inst2.foo = 2;
+ if (inst3->__field_mgr.header->embedded_pool != inst3->__field_mgr_pool->prev) {
+ ast_test_status_update(test, "Structure 3 embedded pool should be the current pool's previous!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ }
- ast_string_field_set(&inst3, string1, "foo");
- ast_string_field_set(&inst3, string2, "baz");
- inst3.foo = 3;
+ ast_test_status_update(test, "Structures 3 additional pool initialized successfully.\n");
- ast_string_field_set(&inst4, string1, "faz");
- ast_string_field_set(&inst4, string2, "baz");
- inst4.foo = 3;
+ ast_string_field_set(inst4, string1, "faz");
+ /* Exhaust the embedded pool */
+ ast_string_field_set(inst4, string2, "baz 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890");
+ inst4->foo = 4;
- if (ast_string_fields_cmp(&inst1, &inst2)) {
+ if (ast_string_fields_cmp(inst1, inst2)) {
ast_test_status_update(test, "Structures 1/2 should be equal!\n");
+ res = AST_TEST_FAIL;
goto error;
} else {
ast_test_status_update(test, "Structures 1/2 are equal as expected.\n");
}
- if (!ast_string_fields_cmp(&inst1, &inst3)) {
+ if (!ast_string_fields_cmp(inst1, inst3)) {
ast_test_status_update(test, "Structures 1/3 should be different!\n");
+ res = AST_TEST_FAIL;
goto error;
} else {
ast_test_status_update(test, "Structures 1/3 are different as expected.\n");
}
- if (!ast_string_fields_cmp(&inst2, &inst3)) {
+ if (!ast_string_fields_cmp(inst2, inst3)) {
ast_test_status_update(test, "Structures 2/3 should be different!\n");
+ res = AST_TEST_FAIL;
goto error;
} else {
ast_test_status_update(test, "Structures 2/3 are different as expected.\n");
}
- if (!ast_string_fields_cmp(&inst3, &inst4)) {
+ if (!ast_string_fields_cmp(inst3, inst4)) {
ast_test_status_update(test, "Structures 3/4 should be different!\n");
+ res = AST_TEST_FAIL;
goto error;
} else {
ast_test_status_update(test, "Structures 3/4 are different as expected.\n");
}
- if (ast_string_fields_copy(&inst1, &inst3)) {
- ast_test_status_update(test, "Copying from structure 3 to structure 4 failed!\n");
+ if (ast_string_fields_copy(inst1, inst3)) {
+ ast_test_status_update(test, "Copying from structure 3 to structure 1 failed!\n");
+ res = AST_TEST_FAIL;
goto error;
} else {
- ast_test_status_update(test, "Copying from structure 3 to structure 4 succeeded!\n");
+ ast_test_status_update(test, "Copying from structure 3 to structure 1 succeeded!\n");
}
/* inst1 and inst3 should now be equal and inst1 should no longer be equal to inst2 */
- if (ast_string_fields_cmp(&inst1, &inst3)) {
+ if (ast_string_fields_cmp(inst1, inst3)) {
ast_test_status_update(test, "Structures 1/3 should be equal!\n");
+ res = AST_TEST_FAIL;
goto error;
} else {
ast_test_status_update(test, "Structures 1/3 are equal as expected.\n");
}
- if (!ast_string_fields_cmp(&inst1, &inst2)) {
+ if (!ast_string_fields_cmp(inst1, inst2)) {
ast_test_status_update(test, "Structures 1/2 should be different!\n");
- goto error;
+ res = AST_TEST_FAIL;
} else {
ast_test_status_update(test, "Structures 1/2 are different as expected.\n");
}
- ast_string_field_free_memory(&inst1);
- ast_string_field_free_memory(&inst2);
- ast_string_field_free_memory(&inst3);
- ast_string_field_free_memory(&inst4);
- return AST_TEST_PASS;
+ ast_test_status_update(test, "Reset but don't free.\n");
+
+ ast_string_field_init(inst1, AST_STRINGFIELD_RESET);
+ ast_string_field_init(inst2, AST_STRINGFIELD_RESET);
+ ast_string_field_init(inst3, AST_STRINGFIELD_RESET);
+ ast_string_field_init(inst4, AST_STRINGFIELD_RESET);
+
+ if (ast_string_fields_cmp(inst1, inst2)) {
+ ast_test_status_update(test, "Structures 1/2 should be the same (empty)!\n");
+ res = AST_TEST_FAIL;
+ } else {
+ ast_test_status_update(test, "Structures 1/2 are the same (empty) as expected.\n");
+ }
+
+ if (inst4->__field_mgr.header->embedded_pool != inst4->__field_mgr_pool) {
+ ast_test_status_update(test, "Structure 4 embedded pool should have been the current pool!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ } else {
+ ast_test_status_update(test, "Structure 4 embedded pool is the current pool as expected.\n");
+ }
+
+ if (inst4->__field_mgr.header->embedded_pool->prev) {
+ ast_test_status_update(test, "Structure 4 embedded pool should not have a previous pool!\n");
+ res = AST_TEST_FAIL;
+ goto error;
+ } else {
+ ast_test_status_update(test, "Structure 4 embedded pool does not have a previous as expected.\n");
+ }
+
error:
- ast_string_field_free_memory(&inst1);
- ast_string_field_free_memory(&inst2);
- ast_string_field_free_memory(&inst3);
- ast_string_field_free_memory(&inst4);
- return AST_TEST_FAIL;
+ ast_string_field_free_memory(inst1);
+ ast_free(inst1);
+ ast_string_field_free_memory(inst2);
+ ast_free(inst2);
+ ast_string_field_free_memory(inst3);
+ ast_free(inst3);
+ ast_string_field_free_memory(inst4);
+ ast_free(inst4);
+
+ return res;
}
static int unload_module(void)