53#define BROADCAST_BUCKETS 37
56#define MAX_REGEX_LENGTH 256
59#define MAX_GROUP_DEPTH 10
62#define MAX_NESTED_QUANTIFIERS 3
65#define MAX_QUANTIFIER_BOUND 100
68#define MAX_ALTERNATIONS 20
71#define ALTERNATION_DEPTH_THRESHOLD 2
74#define MAX_BROADCAST_TIMEOUT_MS (24 * 60 * 60 * 1000)
77#define BROADCAST_POLL_INTERVAL_MS 200
124 .
type =
"stasis_broadcast_context",
172 "Channel %s: rejecting app_filter regex as potentially dangerous: %s\n",
175 REG_EXTENDED | REG_NOSUB) != 0) {
177 "Channel %s: failed to compile app_filter regex '%s'\n",
185 "Channel %s: proceeding without application filtering due to invalid regex\n",
190 ast_debug(1,
"Created broadcast context for channel %s (filter: %s, flags: 0x%x)\n",
211 int quantified_groups = 0;
222 len = strlen(pattern);
224 ast_debug(3,
"Regex pattern exceeds maximum length of %d characters (got %zu)\n",
239 for (p = pattern; *p; p++) {
241 if (!in_class && *p ==
'[' && (p == pattern || *(p - 1) !=
'\\')) {
247 if (*(p + 1) ==
'^') {
250 if (*(p + 1) ==
']') {
254 }
else if (in_class) {
272 ast_debug(3,
"Regex pattern has too many nested groups (max %d)\n",
277 alternations_per_depth[group_depth] = 0;
280 if (group_depth > 0) {
282 alternations_per_depth[group_depth] = 0;
293 if (p > pattern && *(p - 1) ==
')') {
299 const char *q = p + 1;
305 if (*q >=
'0' && *q <=
'9') {
307 while (*q >=
'0' && *q <=
'9') {
309 if (m > (LONG_MAX -
digit) / 10) {
313 m = (m * 10) +
digit;
320 if (!overflow && *q ==
',') {
322 if (*q >=
'0' && *q <=
'9') {
324 while (*q >=
'0' && *q <=
'9') {
326 if (nn > (LONG_MAX -
digit) / 10) {
330 nn = (nn * 10) +
digit;
341 }
else if (!overflow) {
344 if (!overflow && *q ==
'}') {
358 if (p > pattern && *(p - 1) ==
')') {
366 if (group_depth > 0) {
367 alternations_per_depth[group_depth]++;
371 "Regex has too many alternations in deep group (depth %d, count %d, max %d)\n",
373 alternations_per_depth[group_depth],
400 ast_debug(3,
"Regex pattern has too many quantified groups (max %d)\n",
420 const char *caller =
NULL;
421 const char *called =
NULL;
434 "type",
"CallBroadcast",
454 ast_debug(2,
"Broadcasting to %d registered Stasis applications\n",
474 if (app_count == 0) {
475 ast_debug(2,
"Channel %s: no Stasis applications registered\n",
480 matching_arr =
ast_malloc(app_count *
sizeof(*matching_arr));
500 ast_debug(2,
"Broadcasting channel %s to %d matching applications\n",
505 for (i = n - 1; i > 0; i--) {
507 char *tmp = matching_arr[i];
508 matching_arr[i] = matching_arr[j];
509 matching_arr[j] = tmp;
517 for (i = 0; i < n; i++) {
518 char *match_name = matching_arr[i];
521 ast_debug(3,
"Sending broadcast to app '%s'\n", match_name);
526 "Channel %s: failed to deep-copy event for app '%s'\n",
551 const char *app_filter,
unsigned int flags)
613 ast_debug(1,
"Starting broadcast for channel %s (timeout: %dms, filter: %s)\n",
652 ast_debug(1,
"No broadcast context found for channel %s\n", channel_id);
662 ast_debug(1,
"Channel %s already claimed by %s (attempt by %s denied)\n",
663 channel_id, ctx->winner_app ? ctx->winner_app :
"(unknown)",
app_name);
668 ast_debug(1,
"Channel %s broadcast already finished (late claim by %s rejected)\n",
674 if (!ctx->winner_app) {
676 "Failed to allocate winner app name for channel %s\n",
682 ast_verb(3,
"Channel %s claimed by application %s\n",
699 "type",
"CallClaimed",
713 if (ctx->filter_compiled &&
714 regexec(&ctx->compiled_filter, app_name_iter,
715 0,
NULL, 0) == REG_NOMATCH) {
785 const char *channel_id;
786 struct timeval deadline;
806 if (timeout_ms < 0) {
814 ast_tv(timeout_ms / 1000, (timeout_ms % 1000) * 1000));
817 while (!ctx->claimed) {
819 struct timespec poll_spec;
826 ast_debug(3,
"Channel %s hung up during broadcast wait\n",
834 if (remaining_ms <= 0) {
835 ast_debug(3,
"Broadcast timeout for channel %s after %dms\n",
836 channel_id, timeout_ms);
841 poll_ms = remaining_ms;
846 poll_spec.tv_sec = now.tv_sec + (poll_ms / 1000);
847 poll_spec.tv_nsec = (long)(now.tv_usec) * 1000L
848 + (long)(poll_ms % 1000) * 1000000L;
849 while (poll_spec.tv_nsec >= 1000000000) {
851 poll_spec.tv_nsec -= 1000000000;
857 "Channel %s: unexpected error waiting for claim: %s (%d)\n",
865 ast_debug(1,
"Channel %s claimed by %s\n",
866 channel_id, ctx->winner_app);
895 ast_debug(3,
"Cleaning up broadcast context for %s\n", channel_id);
908 ast_debug(1,
"Stasis broadcast module loaded\n");
922 ast_debug(1,
"Stasis broadcast module unloaded\n");
927 "Stasis application broadcast",
931 .
requires =
"res_stasis,res_ari,http",
Asterisk main include file. File version handling, generic pbx functions.
#define ast_strdup(str)
A wrapper for strdup()
#define ast_strdupa(s)
duplicate a string in memory from the stack
#define ast_malloc(len)
A wrapper for malloc()
#define ao2_iterator_next(iter)
#define ao2_link(container, obj)
Add an object to a container.
@ AO2_ALLOC_OPT_LOCK_MUTEX
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define AO2_STRING_FIELD_CMP_FN(stype, field)
Creates a compare function for a structure string field.
#define ao2_unlink(container, obj)
Remove an object from a container.
#define ao2_find(container, arg, flags)
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
void * ao2_object_get_lockaddr(void *obj)
Return the mutex lock address of an object.
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
#define AO2_STRING_FIELD_HASH_FN(stype, field)
Creates a hash function for a structure string field.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
#define ao2_alloc(data_size, destructor_fn)
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
#define AST_MAX_PUBLIC_UNIQUEID
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
#define ast_channel_lock(chan)
const char * ast_channel_uniqueid(const struct ast_channel *chan)
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
const char * ast_channel_exten(const struct ast_channel *chan)
#define ast_channel_unlock(chan)
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
#define ast_datastore_alloc(info, uid)
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
static char * wait_result(void)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
struct ast_channel_snapshot * ast_channel_snapshot_get_latest(const char *uniqueid)
Obtain the latest ast_channel_snapshot from the Stasis Message Bus API cache. This is an ao2 object,...
Support for Private Asterisk HTTP Servers.
#define ast_debug(level,...)
Log a DEBUG message.
#define ast_verb(level,...)
Asterisk JSON abstraction layer.
struct ast_json * ast_json_deep_copy(const struct ast_json *value)
Copy a JSON value, and its children.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
struct ast_json * ast_json_timeval(const struct timeval tv, const char *zone)
Construct a timeval as JSON.
Asterisk locking-related definitions:
#define ast_cond_destroy(cond)
#define ast_cond_init(cond, attr)
#define ast_cond_timedwait(cond, mutex, time)
pthread_cond_t ast_cond_t
#define ast_cond_signal(cond)
Asterisk module definitions.
@ AST_MODFLAG_GLOBAL_SYMBOLS
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
@ AST_MODULE_SUPPORT_EXTENDED
#define ASTERISK_GPL_KEY
The text the key() function should return.
@ AST_MODULE_LOAD_SUCCESS
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
#define AST_OPTIONAL_API_NAME(name)
Expands to the name of the implementation function.
Core PBX routines and definitions.
const char * app_name(struct ast_app *app)
#define MAX_GROUP_DEPTH
Maximum depth for regex group nesting.
void AST_OPTIONAL_API_NAME() stasis_app_broadcast_cleanup(const char *channel_id)
Clean up broadcast context for a channel.
int AST_OPTIONAL_API_NAME() stasis_app_broadcast_wait(struct ast_channel *chan, int timeout_ms)
Wait for a broadcast channel to be claimed.
static void broadcast_ctx_destructor(void *obj)
Destructor for broadcast context.
static int send_broadcast_event(struct ast_channel *chan, struct stasis_broadcast_ctx *ctx)
Create and send broadcast event to all applications.
#define ALTERNATION_DEPTH_THRESHOLD
Group depth threshold for alternation limits.
static int validate_regex_pattern(const char *pattern)
Validate a regex pattern for safety.
#define MAX_BROADCAST_TIMEOUT_MS
Maximum broadcast timeout in milliseconds (24 hours)
static struct ao2_container * broadcast_contexts
Container for all active broadcast contexts.
#define MAX_QUANTIFIER_BOUND
Maximum value for brace quantifier bounds {m,n}.
static void broadcast_datastore_destroy(void *data)
Destructor for broadcast datastore.
#define MAX_NESTED_QUANTIFIERS
Maximum number of nested quantifiers in regex.
static int load_module(void)
int AST_OPTIONAL_API_NAME() stasis_app_broadcast_channel(struct ast_channel *chan, int timeout_ms, const char *app_filter, unsigned int flags)
Start a broadcast for a channel.
#define BROADCAST_POLL_INTERVAL_MS
Interval in ms between hangup checks while waiting for a claim.
static const struct ast_datastore_info broadcast_datastore_info
Datastore information for broadcast context.
static int unload_module(void)
#define MAX_ALTERNATIONS
Maximum alternations allowed in deeply nested groups.
char *AST_OPTIONAL_API_NAME() stasis_app_broadcast_winner(const char *channel_id)
Get the winner app name for a broadcast channel.
static struct stasis_broadcast_ctx * broadcast_ctx_create(const char *channel_id, const char *app_filter, unsigned int flags)
Create a new broadcast context.
#define BROADCAST_BUCKETS
int AST_OPTIONAL_API_NAME() stasis_app_claim_channel(const char *channel_id, const char *app_name)
Attempt to claim a broadcast channel.
#define MAX_REGEX_LENGTH
Maximum length for app_filter regex pattern.
Stasis Application API. See Stasis Application API for detailed documentation.
int stasis_app_send(const char *app_name, struct ast_json *message)
Send a message to the given Stasis application.
struct ao2_container * stasis_app_get_all(void)
Gets the names of all registered Stasis applications.
Stasis Application Broadcast API.
#define STASIS_BROADCAST_FLAG_SUPPRESS_CLAIMED
Suppress CallClaimed event for this broadcast.
Backend API for implementing components of res_stasis.
struct ast_json * ast_channel_snapshot_to_json(const struct ast_channel_snapshot *snapshot, const struct stasis_message_sanitizer *sanitize)
Build a JSON object from a ast_channel_snapshot.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
static force_inline int attribute_pure ast_strlen_zero(const char *s)
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Registered applications container.
Structure representing a snapshot of channel state.
Main Channel structure associated with a channel.
Structure for a data store type.
Structure for a data store object.
Abstract JSON element (object, array, string, int, ...).
Broadcast context stored on channel.
char app_filter[MAX_REGEX_LENGTH+1]
struct ao2_container * container
unsigned int filter_compiled
char channel_id[AST_MAX_PUBLIC_UNIQUEID]
Time-related functions and macros.
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
struct timeval ast_tv(ast_time_t sec, ast_suseconds_t usec)
Returns a timeval from sec, usec.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
long int ast_random(void)