Asterisk - The Open Source Telephony Project  21.4.1
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 <asterisk@phreaknet.org>
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 <asterisk@phreaknet.org>
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  <since>
42  <version>16.21.0</version>
43  <version>18.7.0</version>
44  <version>19.0.0</version>
45  </since>
46  <synopsis>
47  Drops specific frame types in the TX or RX direction on a channel.
48  </synopsis>
49  <syntax>
50  <parameter name="direction" required="true">
51  <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>
52  <para>Subsequent calls to this function will replace previous settings, allowing certain frames to be dropped only temporarily, for instance.</para>
53  <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:
54  for instance, if you drop ANSWER control frames, you should explicitly use <literal>Progress()</literal> for your call or undesired behavior
55  may occur.</para>
56  <enumlist>
57  <enum name = "DTMF_BEGIN" />
58  <enum name = "DTMF_END" />
59  <enum name = "VOICE" />
60  <enum name = "VIDEO" />
61  <enum name = "CONTROL" />
62  <enum name = "NULL" />
63  <enum name = "IAX" />
64  <enum name = "TEXT" />
65  <enum name = "TEXT_DATA" />
66  <enum name = "IMAGE" />
67  <enum name = "HTML" />
68  <enum name = "CNG" />
69  <enum name = "MODEM" />
70  </enumlist>
71  <para>The following CONTROL frames can also be dropped:</para>
72  <enumlist>
73  <enum name = "RING" />
74  <enum name = "RINGING" />
75  <enum name = "ANSWER" />
76  <enum name = "BUSY" />
77  <enum name = "TAKEOFFHOOK" />
78  <enum name = "OFFHOOK" />
79  <enum name = "CONGESTION" />
80  <enum name = "FLASH" />
81  <enum name = "WINK" />
82  <enum name = "PROGRESS" />
83  <enum name = "PROCEEDING" />
84  <enum name = "HOLD" />
85  <enum name = "UNHOLD" />
86  <enum name = "VIDUPDATE" />
87  <enum name = "CONNECTED_LINE" />
88  <enum name = "REDIRECTING" />
89  </enumlist>
90  </parameter>
91  </syntax>
92  <description>
93  <para>Examples:</para>
94  <example title="Drop only DTMF frames towards this channel">
95  exten => 1,1,Set(FRAME_DROP(TX)=DTMF_BEGIN,DTMF_END)
96  </example>
97  <example title="Drop only Answer control frames towards this channel">
98  exten => 1,1,Set(FRAME_DROP(TX)=ANSWER)
99  </example>
100  <example title="Drop only DTMF frames received on this channel">
101  exten => 1,1,Set(FRAME_DROP(RX)=DTMF_BEGIN,DTMF_END)
102  </example>
103  </description>
104  </function>
105  ***/
106 
107 static struct {
108  enum ast_frame_type type;
109  const char *str;
110 } frametype2str[] = {
111  { AST_FRAME_DTMF_BEGIN, ",DTMF_BEGIN," },
112  { AST_FRAME_DTMF_END, ",DTMF_END," },
113  { AST_FRAME_VOICE, ",VOICE," },
114  { AST_FRAME_VIDEO, ",VIDEO," },
115  { AST_FRAME_CONTROL, ",CONTROL," },
116  { AST_FRAME_NULL, ",NULL," },
117  { AST_FRAME_IAX, ",IAX," },
118  { AST_FRAME_TEXT, ",TEXT," },
119  { AST_FRAME_TEXT_DATA, ",TEXT_DATA," },
120  { AST_FRAME_IMAGE, ",IMAGE," },
121  { AST_FRAME_HTML, ",HTML," },
122  { AST_FRAME_CNG, ",CNG," },
123  { AST_FRAME_MODEM, ",MODEM," },
124 };
125 
126 static struct {
127  int type;
128  const char *str;
129 } controlframetype2str[] = {
130  { AST_CONTROL_RING, ",RING," },
131  { AST_CONTROL_RINGING, ",RINGING," },
132  { AST_CONTROL_ANSWER, ",ANSWER," },
133  { AST_CONTROL_BUSY, ",BUSY," },
134  { AST_CONTROL_TAKEOFFHOOK, ",TAKEOFFHOOK," },
135  { AST_CONTROL_OFFHOOK, ",OFFHOOK," },
136  { AST_CONTROL_CONGESTION, ",CONGESTION," },
137  { AST_CONTROL_FLASH, ",FLASH," },
138  { AST_CONTROL_WINK, ",WINK," },
139  { AST_CONTROL_PROGRESS, ",PROGRESS," },
140  { AST_CONTROL_PROCEEDING, ",PROCEEDING," },
141  { AST_CONTROL_HOLD, ",HOLD," },
142  { AST_CONTROL_UNHOLD, ",UNHOLD," },
143  { AST_CONTROL_VIDUPDATE, ",VIDUPDATE," },
144  { AST_CONTROL_CONNECTED_LINE, ",CONNECTED_LINE," },
145  { AST_CONTROL_REDIRECTING, ",REDIRECTING," },
146 };
147 
148 enum direction {
149  TX = 0,
150  RX,
151 };
152 
154  enum direction list_type;
155  int values[ARRAY_LEN(frametype2str)];
156  int controlvalues[ARRAY_LEN(controlframetype2str)];
157 };
158 
159 static void datastore_destroy_cb(void *data) {
160  ast_free(data);
161 }
162 
163 static const struct ast_datastore_info frame_drop_datastore = {
164  .type = "framedrop",
165  .destroy = datastore_destroy_cb
166 };
167 
168 static void hook_destroy_cb(void *framedata)
169 {
170  ast_free(framedata);
171 }
172 
173 static struct ast_frame *hook_event_cb(struct ast_channel *chan, struct ast_frame *frame, enum ast_framehook_event event, void *data)
174 {
175  int i;
176  int drop_frame = 0;
177  struct frame_drop_data *framedata = data;
178  if (!frame) {
179  return frame;
180  }
181 
182  if (!((event == AST_FRAMEHOOK_EVENT_WRITE && framedata->list_type == TX) ||
183  (event == AST_FRAMEHOOK_EVENT_READ && framedata->list_type == RX))) {
184  return frame;
185  }
186 
187  if (frame->frametype == AST_FRAME_CONTROL) {
188  for (i = 0; i < ARRAY_LEN(controlframetype2str); i++) {
189  if (frame->subclass.integer == controlframetype2str[i].type) {
190  if (framedata->controlvalues[i]) {
191  drop_frame = 1;
192  }
193  break;
194  }
195  }
196  } else {
197  for (i = 0; i < ARRAY_LEN(frametype2str); i++) {
198  if (frame->frametype == frametype2str[i].type) {
199  if (framedata->values[i]) {
200  drop_frame = 1;
201  }
202  break;
203  }
204  }
205  }
206 
207  if (drop_frame) {
208  ast_frfree(frame);
209  frame = &ast_null_frame;
210  }
211  return frame;
212 }
213 
214 static int frame_drop_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value)
215 {
216  char *buffer;
217  struct frame_drop_data *framedata;
218  struct ast_datastore *datastore = NULL;
219  struct ast_framehook_interface interface = {
220  .version = AST_FRAMEHOOK_INTERFACE_VERSION,
221  .event_cb = hook_event_cb,
222  .destroy_cb = hook_destroy_cb,
223  };
224  int i = 0;
225 
226  if (!(framedata = ast_calloc(1, sizeof(*framedata)))) {
227  return 0;
228  }
229 
230  interface.data = framedata;
231 
232  if (!strcasecmp(data, "RX")) {
233  framedata->list_type = RX;
234  } else {
235  framedata->list_type = TX;
236  }
237 
238  buffer = ast_malloc(sizeof(value) + 3); /* leading and trailing comma and null terminator */
239  snprintf(buffer, sizeof(value) + 2, ",%s,", value);
240  for (i = 0; i < ARRAY_LEN(frametype2str); i++) {
241  if (strcasestr(buffer, frametype2str[i].str)) {
242  framedata->values[i] = 1;
243  }
244  }
245 
246  for (i = 0; i < ARRAY_LEN(controlframetype2str); i++) {
247  if (strcasestr(buffer, controlframetype2str[i].str)) {
248  framedata->controlvalues[i] = 1;
249  }
250  }
251  ast_free(buffer);
252 
253  ast_channel_lock(chan);
254  i = ast_framehook_attach(chan, &interface);
255  if (i >= 0) {
256  int *id;
257  if ((datastore = ast_channel_datastore_find(chan, &frame_drop_datastore, NULL))) {
258  id = datastore->data;
259  ast_framehook_detach(chan, *id);
260  ast_channel_datastore_remove(chan, datastore);
261  ast_datastore_free(datastore);
262  }
263 
264  if (!(datastore = ast_datastore_alloc(&frame_drop_datastore, NULL))) {
265  ast_framehook_detach(chan, i);
266  ast_channel_unlock(chan);
267  return 0;
268  }
269 
270  if (!(id = ast_calloc(1, sizeof(int)))) {
271  ast_datastore_free(datastore);
272  ast_framehook_detach(chan, i);
273  ast_channel_unlock(chan);
274  return 0;
275  }
276 
277  *id = i; /* Store off the id. The channel is still locked so it is safe to access this ptr. */
278  datastore->data = id;
279  ast_channel_datastore_add(chan, datastore);
280  }
281  ast_channel_unlock(chan);
282 
283  return 0;
284 }
285 
286 static struct ast_custom_function frame_drop_function = {
287  .name = "FRAME_DROP",
288  .write = frame_drop_helper,
289 };
290 
291 static int unload_module(void)
292 {
293  return ast_custom_function_unregister(&frame_drop_function);
294 }
295 
296 static int load_module(void)
297 {
298  int res = ast_custom_function_register(&frame_drop_function);
300 }
301 
302 AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Function to drop frames on a channel.");
const char * name
Definition: pbx.h:119
const char * type
Definition: datastore.h:32
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
ast_framehook_event
These are the types of events that the framehook'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:64
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:2399
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
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
General Asterisk PBX channel definitions.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
Core PBX routines and definitions.
ast_frame_type
Frame types.
union ast_frame::@224 data
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
struct ast_frame ast_null_frame
Definition: main/frame.c:79
FrameHook Architecture.
void * data
Definition: datastore.h:66
Data structure associated with a single frame of data.
enum ast_frame_type frametype
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
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:2385
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2394
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558