]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
ftmod_libpri: Add MSN/DDI filter for incoming calls.
authorStefan Knoblich <stkn@openisdn.net>
Tue, 26 Jun 2012 19:33:40 +0000 (21:33 +0200)
committerStefan Knoblich <stkn@openisdn.net>
Tue, 26 Jun 2012 19:53:20 +0000 (21:53 +0200)
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>
libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c
libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.h

index aef0d0ddffab6f74d0e1ed7111de235789789f34..8428437c3cabd757ed32e1a5f48f2dfc2de5d079 100644 (file)
 #define MIN(x,y)       (((x) < (y)) ? (x) : (y))
 #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)
 {
        assert(chan);
@@ -258,8 +263,145 @@ out:
        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 =
        "Usage:\n"
@@ -268,6 +410,7 @@ static const char *ftdm_libpri_usage =
        "libpri restart <span> <channel/all>\n"
        "libpri maintenance <span> <channel/all> <in/maint/out>\n"
        "libpri debug <span> [all|none|flag,...flagN]\n"
+       "libpri msn <span>\n"
        "\n"
        "Possible debug flags:\n"
        "\tq921_raw     - Q.921 Raw messages\n"
@@ -287,6 +430,29 @@ static const char *ftdm_libpri_usage =
        "\tnone         - Disable debugging\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
  * \param stream API stream handler
@@ -331,6 +497,40 @@ static FIO_API_FUNCTION(ftdm_libpri_api)
                                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) {
                if (!strcasecmp(argv[0], "debug")) {
                        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_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;
        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) {
-               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;
        }
 
@@ -2216,7 +2434,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
 #ifndef HAVE_LIBPRI_BRI
                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));
-               return FTDM_FAIL;
+               goto error;
 #endif
        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");
@@ -2232,7 +2450,16 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
        default:
                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));
-               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++) {
@@ -2247,7 +2474,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
                if (ftdm_strlen_zero(val)) {
                        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);
-                       return FTDM_FAIL;
+                       goto error;
                }
 
                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;
                        }
                }
+               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 {
                        ftdm_log(FTDM_LOG_ERROR, "Unknown parameter '%s', aborting configuration\n", 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;
+error:
+       msn_filter_destroy(isdn_data);
+       ftdm_safe_free(isdn_data);
+       return FTDM_FAIL;
 }
 
 /**
index 9b2c5d09d253a0e64883dd42e59845a843d4c3af..260df0fe35cbaafac64cfc0848f0e73bd81f5f9b 100644 (file)
@@ -76,6 +76,10 @@ struct ftdm_libpri_data {
        unsigned int service_message_support;
 
        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;