mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-04-24 11:48:48 +00:00
ftmod_libpri: Add MSN/DDI filter for incoming calls.
This feature allows ftmod_libpri to ignore calls with non-matching destination number. You may want to use this on BRI PTMP lines (Point-to-MultiPoint), to avoid conflicts between your FreeSWITCH server and other devices connected to the line. The filter is disabled by default (all calls will be accepted), setting one (or more) "local-number" parameters on the span configuration enables it. Example configuration snippet: <libpri_spans> <span name="example01"> <!-- ... other span settings omitted ... --> <param name="local-number" value="123456"/> <param name="local-number" value="654321"/> </span> </libpri_spans> Signed-off-by: Stefan Knoblich <stkn@openisdn.net>
This commit is contained in:
parent
1ae88d51d0
commit
c92a55d3f1
@ -38,6 +38,11 @@
|
|||||||
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
|
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static ftdm_status_t ftdm_libpri_start(ftdm_span_t *span);
|
||||||
|
static ftdm_io_interface_t ftdm_libpri_interface;
|
||||||
|
|
||||||
|
|
||||||
static void _ftdm_channel_set_state_force(ftdm_channel_t *chan, const ftdm_channel_state_t state)
|
static void _ftdm_channel_set_state_force(ftdm_channel_t *chan, const ftdm_channel_state_t state)
|
||||||
{
|
{
|
||||||
assert(chan);
|
assert(chan);
|
||||||
@ -258,8 +263,145 @@ out:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ftdm_status_t ftdm_libpri_start(ftdm_span_t *span);
|
|
||||||
static ftdm_io_interface_t ftdm_libpri_interface;
|
/***************************************************************
|
||||||
|
* MSN filter
|
||||||
|
***************************************************************/
|
||||||
|
|
||||||
|
static int msn_filter_init(ftdm_libpri_data_t *isdn_data)
|
||||||
|
{
|
||||||
|
if (!isdn_data)
|
||||||
|
return FTDM_FAIL;
|
||||||
|
|
||||||
|
isdn_data->msn_hash = create_hashtable(16, ftdm_hash_hashfromstring, ftdm_hash_equalkeys);
|
||||||
|
if (!isdn_data->msn_hash)
|
||||||
|
return FTDM_FAIL;
|
||||||
|
|
||||||
|
if (ftdm_mutex_create(&isdn_data->msn_mutex)) {
|
||||||
|
hashtable_destroy(isdn_data->msn_hash);
|
||||||
|
return FTDM_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FTDM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int msn_filter_destroy(ftdm_libpri_data_t *isdn_data)
|
||||||
|
{
|
||||||
|
if (!isdn_data)
|
||||||
|
return FTDM_FAIL;
|
||||||
|
|
||||||
|
if (isdn_data->msn_hash)
|
||||||
|
hashtable_destroy(isdn_data->msn_hash);
|
||||||
|
if (isdn_data->msn_mutex)
|
||||||
|
ftdm_mutex_destroy(&isdn_data->msn_mutex);
|
||||||
|
|
||||||
|
return FTDM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int msn_filter_verify(const char *str)
|
||||||
|
{
|
||||||
|
if (ftdm_strlen_zero(str) || strlen(str) >= FTDM_DIGITS_LIMIT)
|
||||||
|
return FTDM_FALSE;
|
||||||
|
|
||||||
|
if (ftdm_is_number(str) != FTDM_SUCCESS)
|
||||||
|
return FTDM_FALSE;
|
||||||
|
|
||||||
|
return FTDM_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int msn_filter_add(ftdm_libpri_data_t *isdn_data, const char *msn)
|
||||||
|
{
|
||||||
|
static const int value = 0xdeadbeef;
|
||||||
|
char *key = NULL;
|
||||||
|
int ret = FTDM_SUCCESS;
|
||||||
|
|
||||||
|
if (!isdn_data || !msn_filter_verify(msn))
|
||||||
|
return FTDM_FAIL;
|
||||||
|
|
||||||
|
ftdm_mutex_lock(isdn_data->msn_mutex);
|
||||||
|
|
||||||
|
/* check for duplicates (ignore if already in set) */
|
||||||
|
if (hashtable_search(isdn_data->msn_hash, (void *)msn)) {
|
||||||
|
ret = FTDM_SUCCESS;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy MSN (transient string), hashtable will free it in hashtable_destroy() */
|
||||||
|
key = ftdm_strdup(msn);
|
||||||
|
if (!key) {
|
||||||
|
ret = FTDM_FAIL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add MSN to list/hash */
|
||||||
|
if (!hashtable_insert(isdn_data->msn_hash, (void *)key, (void *)&value, HASHTABLE_FLAG_FREE_KEY)) {
|
||||||
|
ftdm_safe_free(key);
|
||||||
|
ret = FTDM_FAIL;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
ftdm_mutex_unlock(isdn_data->msn_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int msn_filter_match(ftdm_libpri_data_t *isdn_data, const char *msn)
|
||||||
|
{
|
||||||
|
int ret = FTDM_FALSE;
|
||||||
|
|
||||||
|
if (!isdn_data)
|
||||||
|
return FTDM_FAIL;
|
||||||
|
/* No number? return match found */
|
||||||
|
if (ftdm_strlen_zero(msn))
|
||||||
|
return FTDM_TRUE;
|
||||||
|
|
||||||
|
ftdm_mutex_lock(isdn_data->msn_mutex);
|
||||||
|
|
||||||
|
/* No MSN configured? */
|
||||||
|
if (hashtable_count(isdn_data->msn_hash) <= 0) {
|
||||||
|
ret = FTDM_TRUE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
/* Search for a matching MSN */
|
||||||
|
if (hashtable_search(isdn_data->msn_hash, (void *)msn))
|
||||||
|
ret = FTDM_TRUE;
|
||||||
|
out:
|
||||||
|
ftdm_mutex_unlock(isdn_data->msn_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int msn_filter_foreach(ftdm_libpri_data_t *isdn_data, int (* func)(const char *, void *), void *data)
|
||||||
|
{
|
||||||
|
ftdm_hash_iterator_t *iter = NULL;
|
||||||
|
int ret = FTDM_SUCCESS;
|
||||||
|
|
||||||
|
if (!isdn_data || !func)
|
||||||
|
return FTDM_FAIL;
|
||||||
|
|
||||||
|
/* iterate over MSNs */
|
||||||
|
ftdm_mutex_lock(isdn_data->msn_mutex);
|
||||||
|
|
||||||
|
for (iter = hashtable_first(isdn_data->msn_hash); iter; iter = hashtable_next(iter)) {
|
||||||
|
const char *msn = NULL;
|
||||||
|
|
||||||
|
hashtable_this(iter, (const void **)&msn, NULL, NULL);
|
||||||
|
|
||||||
|
if (ftdm_strlen_zero(msn))
|
||||||
|
break;
|
||||||
|
if ((ret = func(msn, data)) != FTDM_SUCCESS)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ftdm_mutex_unlock(isdn_data->msn_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* Module API (CLI) interface
|
||||||
|
***************************************************************/
|
||||||
|
|
||||||
static const char *ftdm_libpri_usage =
|
static const char *ftdm_libpri_usage =
|
||||||
"Usage:\n"
|
"Usage:\n"
|
||||||
@ -268,6 +410,7 @@ static const char *ftdm_libpri_usage =
|
|||||||
"libpri restart <span> <channel/all>\n"
|
"libpri restart <span> <channel/all>\n"
|
||||||
"libpri maintenance <span> <channel/all> <in/maint/out>\n"
|
"libpri maintenance <span> <channel/all> <in/maint/out>\n"
|
||||||
"libpri debug <span> [all|none|flag,...flagN]\n"
|
"libpri debug <span> [all|none|flag,...flagN]\n"
|
||||||
|
"libpri msn <span>\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Possible debug flags:\n"
|
"Possible debug flags:\n"
|
||||||
"\tq921_raw - Q.921 Raw messages\n"
|
"\tq921_raw - Q.921 Raw messages\n"
|
||||||
@ -287,6 +430,29 @@ static const char *ftdm_libpri_usage =
|
|||||||
"\tnone - Disable debugging\n"
|
"\tnone - Disable debugging\n"
|
||||||
"\tall - Enable all debug options\n";
|
"\tall - Enable all debug options\n";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom data handle for list iterator functions
|
||||||
|
*/
|
||||||
|
struct msn_list_cb_private {
|
||||||
|
ftdm_stream_handle_t *stream;
|
||||||
|
unsigned int count;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int msn_list_cb(const char *msn, void *priv)
|
||||||
|
{
|
||||||
|
struct msn_list_cb_private *data = priv;
|
||||||
|
ftdm_stream_handle_t *stream = data->stream;
|
||||||
|
|
||||||
|
if (!stream || ftdm_strlen_zero(msn))
|
||||||
|
return FTDM_FAIL;
|
||||||
|
|
||||||
|
stream->write_function(stream, "\t%s\n", msn);
|
||||||
|
data->count++;
|
||||||
|
return FTDM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief API function to kill or debug a libpri span
|
* \brief API function to kill or debug a libpri span
|
||||||
* \param stream API stream handler
|
* \param stream API stream handler
|
||||||
@ -331,6 +497,40 @@ static FIO_API_FUNCTION(ftdm_libpri_api)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!strcasecmp(argv[0], "msn")) {
|
||||||
|
ftdm_span_t *span = NULL;
|
||||||
|
struct msn_list_cb_private data;
|
||||||
|
data.stream = stream;
|
||||||
|
data.count = 0;
|
||||||
|
|
||||||
|
if (ftdm_span_find_by_name(argv[1], &span) != FTDM_SUCCESS) {
|
||||||
|
stream->write_function(stream, "%s: -ERR span '%s' not found.\n",
|
||||||
|
__FILE__, argv[1]);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (span->start != ftdm_libpri_start) {
|
||||||
|
stream->write_function(stream, "%s: -ERR '%s' is not a libpri span.\n",
|
||||||
|
__FILE__, ftdm_span_get_name(span));
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* header */
|
||||||
|
stream->write_function(stream, "------------------------------------------------------------------------------\n");
|
||||||
|
|
||||||
|
if (msn_filter_foreach(span->signal_data, msn_list_cb, &data)) {
|
||||||
|
stream->write_function(stream, "-ERR: Failed to list MSN(s)\n");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (data.count == 0) {
|
||||||
|
stream->write_function(stream, "\t\t\t -- no entries --\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* footer */
|
||||||
|
stream->write_function(stream, "---------------------------------------------------------------[ %02d MSN(s) ]--\n",
|
||||||
|
data.count);
|
||||||
|
stream->write_function(stream, "+OK");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
} else if (argc >= 2) {
|
} else if (argc >= 2) {
|
||||||
if (!strcasecmp(argv[0], "debug")) {
|
if (!strcasecmp(argv[0], "debug")) {
|
||||||
ftdm_span_t *span = NULL;
|
ftdm_span_t *span = NULL;
|
||||||
@ -1309,12 +1509,30 @@ static int on_ring(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event
|
|||||||
{
|
{
|
||||||
ftdm_span_t *span = spri->span;
|
ftdm_span_t *span = spri->span;
|
||||||
ftdm_libpri_data_t *isdn_data = span->signal_data;
|
ftdm_libpri_data_t *isdn_data = span->signal_data;
|
||||||
ftdm_channel_t *chan = ftdm_span_get_channel(span, pevent->ring.channel);
|
ftdm_channel_t *chan = NULL;
|
||||||
ftdm_caller_data_t *caller_data = NULL;
|
ftdm_caller_data_t *caller_data = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
if (pevent->ring.channel == -1) {
|
||||||
|
ftdm_log(FTDM_LOG_ERROR, "-- New call without channel on span '%s' [NOTE: Initial SETUP w/o channel selection is not supported by FreeTDM]\n",
|
||||||
|
ftdm_span_get_name(span));
|
||||||
|
pri_destroycall(spri->pri, pevent->ring.call);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
chan = ftdm_span_get_channel(span, pevent->ring.channel);
|
||||||
if (!chan) {
|
if (!chan) {
|
||||||
ftdm_log(FTDM_LOG_ERROR, "-- Unable to get channel %d:%d\n", ftdm_span_get_id(span), pevent->ring.channel);
|
ftdm_log(FTDM_LOG_ERROR, "-- Unable to get channel %d:%d\n",
|
||||||
|
ftdm_span_get_id(span), pevent->ring.channel);
|
||||||
|
pri_hangup(spri->pri, pevent->ring.call, PRI_CAUSE_DESTINATION_OUT_OF_ORDER);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check MSN filter */
|
||||||
|
if (!msn_filter_match(isdn_data, pevent->ring.callednum)) {
|
||||||
|
ftdm_log(FTDM_LOG_INFO, "-- MSN filter not matching incoming DNIS '%s', ignoring call\n",
|
||||||
|
pevent->ring.callednum);
|
||||||
|
pri_destroycall(spri->pri, pevent->ring.call);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2216,7 +2434,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
|
|||||||
#ifndef HAVE_LIBPRI_BRI
|
#ifndef HAVE_LIBPRI_BRI
|
||||||
ftdm_log(FTDM_LOG_ERROR, "Unsupported trunk type: '%s', libpri too old\n", ftdm_span_get_trunk_type_str(span));
|
ftdm_log(FTDM_LOG_ERROR, "Unsupported trunk type: '%s', libpri too old\n", ftdm_span_get_trunk_type_str(span));
|
||||||
snprintf(span->last_error, sizeof(span->last_error), "Unsupported trunk type [%s], libpri too old", ftdm_span_get_trunk_type_str(span));
|
snprintf(span->last_error, sizeof(span->last_error), "Unsupported trunk type [%s], libpri too old", ftdm_span_get_trunk_type_str(span));
|
||||||
return FTDM_FAIL;
|
goto error;
|
||||||
#endif
|
#endif
|
||||||
case FTDM_TRUNK_E1:
|
case FTDM_TRUNK_E1:
|
||||||
ftdm_log(FTDM_LOG_NOTICE, "Setting default Layer 1 to ALAW since this is an E1/BRI/BRI PTMP trunk\n");
|
ftdm_log(FTDM_LOG_NOTICE, "Setting default Layer 1 to ALAW since this is an E1/BRI/BRI PTMP trunk\n");
|
||||||
@ -2232,7 +2450,16 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
|
|||||||
default:
|
default:
|
||||||
ftdm_log(FTDM_LOG_ERROR, "Invalid trunk type: '%s'\n", ftdm_span_get_trunk_type_str(span));
|
ftdm_log(FTDM_LOG_ERROR, "Invalid trunk type: '%s'\n", ftdm_span_get_trunk_type_str(span));
|
||||||
snprintf(span->last_error, sizeof(span->last_error), "Invalid trunk type [%s]", ftdm_span_get_trunk_type_str(span));
|
snprintf(span->last_error, sizeof(span->last_error), "Invalid trunk type [%s]", ftdm_span_get_trunk_type_str(span));
|
||||||
return FTDM_FAIL;
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init MSN filter
|
||||||
|
*/
|
||||||
|
if (msn_filter_init(isdn_data) != FTDM_SUCCESS) {
|
||||||
|
ftdm_log(FTDM_LOG_ERROR, "Failed to init MSN filter\n");
|
||||||
|
snprintf(span->last_error, sizeof(span->last_error), "Failed to init MSN filter");
|
||||||
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; ftdm_parameters[i].var; i++) {
|
for (i = 0; ftdm_parameters[i].var; i++) {
|
||||||
@ -2247,7 +2474,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
|
|||||||
if (ftdm_strlen_zero(val)) {
|
if (ftdm_strlen_zero(val)) {
|
||||||
ftdm_log(FTDM_LOG_ERROR, "Parameter '%s' has no value\n", var);
|
ftdm_log(FTDM_LOG_ERROR, "Parameter '%s' has no value\n", var);
|
||||||
snprintf(span->last_error, sizeof(span->last_error), "Parameter [%s] has no value", var);
|
snprintf(span->last_error, sizeof(span->last_error), "Parameter [%s] has no value", var);
|
||||||
return FTDM_FAIL;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcasecmp(var, "node") || !strcasecmp(var, "mode")) {
|
if (!strcasecmp(var, "node") || !strcasecmp(var, "mode")) {
|
||||||
@ -2285,10 +2512,17 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
|
|||||||
isdn_data->service_message_support = 1;
|
isdn_data->service_message_support = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (!strcasecmp(var, "local-number") || !strcasecmp(var, "msn")) {
|
||||||
|
if (msn_filter_add(isdn_data, val) != FTDM_SUCCESS) {
|
||||||
|
ftdm_log(FTDM_LOG_ERROR, "Invalid MSN/DDI(s) '%s' specified\n", val);
|
||||||
|
snprintf(span->last_error, sizeof(span->last_error), "Invalid MSN/DDI(s) '%s' specified!", val);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
ftdm_log(FTDM_LOG_ERROR, "Unknown parameter '%s', aborting configuration\n", var);
|
ftdm_log(FTDM_LOG_ERROR, "Unknown parameter '%s', aborting configuration\n", var);
|
||||||
snprintf(span->last_error, sizeof(span->last_error), "Unknown parameter [%s]", var);
|
snprintf(span->last_error, sizeof(span->last_error), "Unknown parameter [%s]", var);
|
||||||
return FTDM_FAIL;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2315,6 +2549,10 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return FTDM_SUCCESS;
|
return FTDM_SUCCESS;
|
||||||
|
error:
|
||||||
|
msn_filter_destroy(isdn_data);
|
||||||
|
ftdm_safe_free(isdn_data);
|
||||||
|
return FTDM_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -76,6 +76,10 @@ struct ftdm_libpri_data {
|
|||||||
unsigned int service_message_support;
|
unsigned int service_message_support;
|
||||||
|
|
||||||
lpwrap_pri_t spri;
|
lpwrap_pri_t spri;
|
||||||
|
|
||||||
|
/* MSN filter */
|
||||||
|
ftdm_hash_t *msn_hash;
|
||||||
|
ftdm_mutex_t *msn_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct ftdm_libpri_data ftdm_libpri_data_t;
|
typedef struct ftdm_libpri_data ftdm_libpri_data_t;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user