Asterisk - The Open Source Telephony Project  18.5.0
func_frame_drop.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2021, Naveen Albert
5  *
6  * Naveen Albert <[email protected]>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  *
21  * \brief Function that drops specified frames from channels
22  *
23  * \author Naveen Albert <[email protected]>
24  *
25  * \ingroup functions
26  */
27 
28 /*** MODULEINFO
29  <support_level>extended</support_level>
30  ***/
31 
32 #include "asterisk.h"
33 
34 #include "asterisk/module.h"
35 #include "asterisk/channel.h"
36 #include "asterisk/pbx.h"
37 #include "asterisk/framehook.h"
38 
39 /*** DOCUMENTATION
40  <function name="FRAME_DROP" language="en_US">
41  <synopsis>
42  Drops specific frame types in the TX or RX direction on a channel.
43  </synopsis>
44  <syntax>
45  <parameter name="direction" required="true">
46  <para>List of frame types to be dropped for the specified direction. Direction can be <literal>TX</literal> or <literal>RX</literal>. The <literal>TX</literal> direction will prevent Asterisk from sending frames to a channel, and the <literal>RX</literal> direction will prevent Asterisk from receiving frames from a channel.</para>
47  <para>Subsequent calls to this function will replace previous settings, allowing certain frames to be dropped only temporarily, for instance.</para>
48  <para>Below are the different types of frames that can be dropped. Other actions may need to be taken in conjunction with use of this function:
49  for instance, if you drop ANSWER control frames, you should explicitly use <literal>Progress()</literal> for your call or undesired behavior
50  may occur.</para>
51  <enumlist>
52  <enum name = "DTMF_BEGIN" />
53  <enum name = "DTMF_END" />
54  <enum name = "VOICE" />
55  <enum name = "VIDEO" />
56  <enum name = "CONTROL" />
57  <enum name = "NULL" />
58  <enum name = "IAX" />
59  <enum name = "TEXT" />
60  <enum name = "TEXT_DATA" />
61  <enum name = "IMAGE" />
62  <enum name = "HTML" />
63  <enum name = "CNG" />
64  <enum name = "MODEM" />
65  </enumlist>
66  <para>The following CONTROL frames can also be dropped:</para>
67  <enumlist>
68  <enum name = "RING" />
69  <enum name = "RINGING" />
70  <enum name = "ANSWER" />
71  <enum name = "BUSY" />
72  <enum name = "TAKEOFFHOOK" />
73  <enum name = "OFFHOOK" />
74  <enum name = "CONGESTION" />
75  <enum name = "FLASH" />
76  <enum name = "WINK" />
77  <enum name = "PROGRESS" />
78  <enum name = "PROCEEDING" />
79  <enum name = "HOLD" />
80  <enum name = "UNHOLD" />
81  <enum name = "VIDUPDATE" />
82  <enum name = "CONNECTED_LINE" />
83  <enum name = "REDIRECTING" />
84  </enumlist>
85  </parameter>
86  </syntax>
87  <description>
88  <para>Examples:</para>
89  <para>exten => 1,1,Set(FRAME_DROP(TX)=DTMF_BEGIN,DTMF_END); drop only DTMF frames towards this channel.</para>
90  <para>exten => 1,1,Set(FRAME_DROP(TX)=ANSWER); drop only ANSWER CONTROL frames towards this channel.</para>
91  <para>exten => 1,1,Set(FRAME_DROP(RX)=DTMF_BEGIN,DTMF_END); drop only DTMF frames received on this channel.</para>
92  </description>
93  </function>
94  ***/
95 
96 static struct {
98  const char *str;
99 } frametype2str[] = {
100  { AST_FRAME_DTMF_BEGIN, ",DTMF_BEGIN," },
101  { AST_FRAME_DTMF_END, ",DTMF_END," },
102  { AST_FRAME_VOICE, ",VOICE," },
103  { AST_FRAME_VIDEO, ",VIDEO," },
104  { AST_FRAME_CONTROL, ",CONTROL," },
105  { AST_FRAME_NULL, ",NULL," },
106  { AST_FRAME_IAX, ",IAX," },
107  { AST_FRAME_TEXT, ",TEXT," },
108  { AST_FRAME_TEXT_DATA, ",TEXT_DATA," },
109  { AST_FRAME_IMAGE, ",IMAGE," },
110  { AST_FRAME_HTML, ",HTML," },
111  { AST_FRAME_CNG, ",CNG," },
112  { AST_FRAME_MODEM, ",MODEM," },
113 };
114 
115 static struct {
116  int type;
117  const char *str;
118 } controlframetype2str[] = {
119  { AST_CONTROL_RING, ",RING," },
120  { AST_CONTROL_RINGING, ",RINGING," },
121  { AST_CONTROL_ANSWER, ",ANSWER," },
122  { AST_CONTROL_BUSY, ",BUSY," },
123  { AST_CONTROL_TAKEOFFHOOK, ",TAKEOFFHOOK," },
124  { AST_CONTROL_OFFHOOK, ",OFFHOOK," },
125  { AST_CONTROL_CONGESTION, ",CONGESTION," },
126  { AST_CONTROL_FLASH, ",FLASH," },
127  { AST_CONTROL_WINK, ",WINK," },
128  { AST_CONTROL_PROGRESS, ",PROGRESS," },
129  { AST_CONTROL_PROCEEDING, ",PROCEEDING," },
130  { AST_CONTROL_HOLD, ",HOLD," },
131  { AST_CONTROL_UNHOLD, ",UNHOLD," },
132  { AST_CONTROL_VIDUPDATE, ",VIDUPDATE," },
133  { AST_CONTROL_CONNECTED_LINE, ",CONNECTED_LINE," },
134  { AST_CONTROL_REDIRECTING, ",REDIRECTING," },
135 };
136 
137 enum direction {
138  TX = 0,
139  RX,
140 };
141 
143  enum direction list_type;
145  int controlvalues[ARRAY_LEN(controlframetype2str)];
146 };
147 
148 static void datastore_destroy_cb(void *data) {
149  ast_free(data);
150 }
151 
153  .type = "framedrop",
154  .destroy = datastore_destroy_cb
155 };
156 
157 static void hook_destroy_cb(void *framedata)
158 {
159  ast_free(framedata);
160 }
161 
162 static struct ast_frame *hook_event_cb(struct ast_channel *chan, struct ast_frame *frame, enum ast_framehook_event event, void *data)
163 {
164  int i;
165  int drop_frame = 0;
166  struct frame_drop_data *framedata = data;
167  if (!frame) {
168  return frame;
169  }
170 
171  if (!((event == AST_FRAMEHOOK_EVENT_WRITE && framedata->list_type == TX) ||
172  (event == AST_FRAMEHOOK_EVENT_READ && framedata->list_type == RX))) {
173  return frame;
174  }
175 
176  if (frame->frametype == AST_FRAME_CONTROL) {
177  for (i = 0; i < ARRAY_LEN(controlframetype2str); i++) {
178  if (frame->subclass.integer == controlframetype2str[i].type) {
179  if (framedata->controlvalues[i]) {
180  drop_frame = 1;
181  }
182  break;
183  }
184  }
185  } else {
186  for (i = 0; i < ARRAY_LEN(frametype2str); i++) {
187  if (frame->frametype == frametype2str[i].type) {
188  if (framedata->values[i]) {
189  drop_frame = 1;
190  }
191  break;
192  }
193  }
194  }
195 
196  if (drop_frame) {
197  ast_frfree(frame);
198  frame = &ast_null_frame;
199  }
200  return frame;
201 }
202 
203 static int frame_drop_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value)
204 {
205  char *buffer;
206  struct frame_drop_data *framedata;
207  struct ast_datastore *datastore = NULL;
208  struct ast_framehook_interface interface = {
210  .event_cb = hook_event_cb,
211  .destroy_cb = hook_destroy_cb,
212  };
213  int i = 0;
214 
215  if (!(framedata = ast_calloc(1, sizeof(*framedata)))) {
216  return 0;
217  }
218 
219  interface.data = framedata;
220 
221  if (!strcasecmp(data, "RX")) {
222  framedata->list_type = RX;
223  } else {
224  framedata->list_type = TX;
225  }
226 
227  buffer = ast_malloc(sizeof(value) + 3); /* leading and trailing comma and null terminator */
228  snprintf(buffer, sizeof(value) + 2, ",%s,", value);
229  for (i = 0; i < ARRAY_LEN(frametype2str); i++) {
230  if (strcasestr(value, frametype2str[i].str)) {
231  framedata->values[i] = 1;
232  }
233  }
234 
235  for (i = 0; i < ARRAY_LEN(controlframetype2str); i++) {
236  if (strcasestr(value, controlframetype2str[i].str)) {
237  framedata->controlvalues[i] = 1;
238  }
239  }
240  ast_free(buffer);
241 
242  ast_channel_lock(chan);
243  i = ast_framehook_attach(chan, &interface);
244  if (i >= 0) {
245  int *id;
246  if ((datastore = ast_channel_datastore_find(chan, &frame_drop_datastore, NULL))) {
247  id = datastore->data;
248  ast_framehook_detach(chan, *id);
249  ast_channel_datastore_remove(chan, datastore);
250  ast_datastore_free(datastore);
251  }
252 
253  if (!(datastore = ast_datastore_alloc(&frame_drop_datastore, NULL))) {
254  ast_framehook_detach(chan, i);
255  ast_channel_unlock(chan);
256  return 0;
257  }
258 
259  if (!(id = ast_calloc(1, sizeof(int)))) {
260  ast_datastore_free(datastore);
261  ast_framehook_detach(chan, i);
262  ast_channel_unlock(chan);
263  return 0;
264  }
265 
266  *id = i; /* Store off the id. The channel is still locked so it is safe to access this ptr. */
267  datastore->data = id;
268  ast_channel_datastore_add(chan, datastore);
269  }
270  ast_channel_unlock(chan);
271 
272  return 0;
273 }
274 
276  .name = "FRAME_DROP",
277  .write = frame_drop_helper,
278 };
279 
280 static int unload_module(void)
281 {
282  return ast_custom_function_unregister(&frame_drop_function);
283 }
284 
285 static int load_module(void)
286 {
287  int res = ast_custom_function_register(&frame_drop_function);
289 }
290 
291 AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Function to drop frames on a channel.");
292 
const char * name
Definition: pbx.h:119
const char * type
Definition: datastore.h:32
static void hook_destroy_cb(void *framedata)
#define ast_channel_lock(chan)
Definition: channel.h:2945
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
static struct ast_custom_function frame_drop_function
enum ast_frame_type type
static struct ast_frame * hook_event_cb(struct ast_channel *chan, struct ast_frame *frame, enum ast_framehook_event event, void *data)
ast_framehook_event
These are the types of events that the framehook&#39;s event callback can receive.
Definition: framehook.h:151
int ast_framehook_detach(struct ast_channel *chan, int framehook_id)
Detach an framehook from a channel.
Definition: framehook.c:177
Structure for a data store type.
Definition: datastore.h:31
Definition: astman.c:222
Structure for a data store object.
Definition: datastore.h:68
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.
Definition: channel.c:2404
#define NULL
Definition: resample.c:96
int value
Definition: syslog.c:37
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
struct ast_frame_subclass subclass
static struct @207 frametype2str[]
int ast_framehook_attach(struct ast_channel *chan, struct ast_framehook_interface *i)
Attach an framehook onto a channel for frame interception.
Definition: framehook.c:132
int controlvalues[ARRAY_LEN(controlframetype2str)]
static int unload_module(void)
General Asterisk PBX channel definitions.
const char * str
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
static void datastore_destroy_cb(void *data)
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
Core PBX routines and definitions.
ast_frame_type
Frame types.
int values[ARRAY_LEN(frametype2str)]
static int frame_drop_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value)
#define AST_FRAMEHOOK_INTERFACE_VERSION
Definition: framehook.h:227
char * strcasestr(const char *, const char *)
#define ast_channel_unlock(chan)
Definition: channel.h:2946
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
enum direction list_type
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static struct @208 controlframetype2str[]
struct ast_frame ast_null_frame
Definition: main/frame.c:79
static int load_module(void)
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Function to drop frames on a channel.")
FrameHook Architecture.
void * data
Definition: datastore.h:70
#define ast_frfree(fr)
Data structure associated with a single frame of data.
static const struct ast_datastore_info frame_drop_datastore
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:89
union ast_frame::@263 data
enum queue_result id
Definition: app_queue.c:1507
enum ast_frame_type frametype
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
direction
Asterisk module definitions.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2390
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2399
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1508