|
| 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