Skip to content

Commit 6a89266

Browse files
InterLinked1jcolp
authored andcommitted
func_frame_drop: New function
Adds function to selectively drop specified frames in the TX or RX direction on a channel, including control frames. ASTERISK-29478 Change-Id: I8147c9d55d74e2e48861edba6b22f930920541ec
1 parent 8a6c9c3 commit 6a89266

File tree

2 files changed

+296
-0
lines changed

2 files changed

+296
-0
lines changed
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Subject: func_framedrop
2+
3+
New function to selectively drop specified frames
4+
in either direction on a channel.
5+

funcs/func_frame_drop.c

+291
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
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+
<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 {
97+
enum ast_frame_type type;
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+
142+
struct frame_drop_data {
143+
enum direction list_type;
144+
int values[ARRAY_LEN(frametype2str)];
145+
int controlvalues[ARRAY_LEN(controlframetype2str)];
146+
};
147+
148+
static void datastore_destroy_cb(void *data) {
149+
ast_free(data);
150+
}
151+
152+
static const struct ast_datastore_info frame_drop_datastore = {
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 = {
209+
.version = AST_FRAMEHOOK_INTERFACE_VERSION,
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+
275+
static struct ast_custom_function frame_drop_function = {
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);
288+
return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
289+
}
290+
291+
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Function to drop frames on a channel.");

0 commit comments

Comments
 (0)