extcap:Replace self-organized lists with glib ones
[metze/wireshark/wip.git] / extcap.c
index 4fbf5d79e05a7787d07aa9cfccc993079b2c07df..2cdbf44051973bba6e3e53dbf9bbad843d924e98 100644 (file)
--- a/extcap.c
+++ b/extcap.c
 #include <unistd.h>
 #endif
 
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
 #include <glib.h>
 #include <log.h>
 
@@ -58,6 +65,7 @@ static HANDLE pipe_h = NULL;
 #endif
 
 #define EXTCAP_PREF_SIZE 256
+static void extcap_child_watch_cb(GPid pid, gint status _U_, gpointer user_data);
 
 /* internal container, for all the extcap interfaces that have been found.
  * will be resetted by every call to extcap_interface_list() and is being
@@ -82,11 +90,6 @@ static GHashTable *extcap_prefs_dynamic_vals = NULL;
 typedef gboolean (*extcap_cb_t)(const gchar *extcap, const gchar *ifname, gchar *output, void *data,
         gchar **err_str);
 
-/* #define ARG_DEBUG */
-#if ARG_DEBUG
-static void extcap_debug_arguments ( extcap_arg *arg_iter );
-#endif
-
 static gboolean
 extcap_if_exists(const gchar *ifname)
 {
@@ -193,18 +196,25 @@ static void extcap_foreach(gint argc, gchar **args, extcap_cb_t cb,
 
 }
 
+static void extcap_free_dlt(gpointer d, gpointer user_data _U_) {
+    if (d == NULL)
+        return;
+
+    g_free(((extcap_dlt *)d)->name);
+    g_free(((extcap_dlt *)d)->display);
+}
+
 static gboolean dlt_cb(const gchar *extcap _U_, const gchar *ifname _U_, gchar *output, void *data,
         char **err_str) {
-    extcap_token_sentence *tokens;
-    extcap_dlt *dlts, *dlt_iter, *next;
+    GList *dlts = NULL, *temp = NULL;
+
     if_capabilities_t *caps;
     GList *linktype_list = NULL;
     data_link_info_t *data_link_info;
+    extcap_dlt * dlt_item;
 
-    tokens = extcap_tokenize_sentences(output);
-    extcap_parse_dlts(tokens, &dlts);
-
-    extcap_free_tokenized_sentence_list(tokens);
+    dlts = extcap_parse_dlts(output);
+    temp = dlts;
 
     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Extcap pipe %s ", extcap);
 
@@ -214,18 +224,21 @@ static gboolean dlt_cb(const gchar *extcap _U_, const gchar *ifname _U_, gchar *
     caps = (if_capabilities_t *) g_malloc(sizeof *caps);
     caps->can_set_rfmon = FALSE;
 
-    dlt_iter = dlts;
-    while (dlt_iter != NULL ) {
-        g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
-                "  DLT %d name=\"%s\" display=\"%s\" ", dlt_iter->number,
-                dlt_iter->name, dlt_iter->display);
-
-        data_link_info = g_new(data_link_info_t, 1);
-        data_link_info->dlt = dlt_iter->number;
-        data_link_info->name = g_strdup(dlt_iter->name);
-        data_link_info->description = g_strdup(dlt_iter->display);
-        linktype_list = g_list_append(linktype_list, data_link_info);
-        dlt_iter = dlt_iter->next_dlt;
+    while (dlts) {
+        dlt_item = (extcap_dlt *)dlts->data;
+        if (dlt_item) {
+            g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+                "  DLT %d name=\"%s\" display=\"%s\" ", dlt_item->number,
+                dlt_item->name, dlt_item->display);
+
+            data_link_info = g_new(data_link_info_t, 1);
+            data_link_info->dlt = dlt_item->number;
+            data_link_info->name = g_strdup(dlt_item->name);
+            data_link_info->description = g_strdup(dlt_item->display);
+            linktype_list = g_list_append(linktype_list, data_link_info);
+        }
+
+        dlts = g_list_next(dlts);
     }
 
     /* Check to see if we built a list */
@@ -240,12 +253,7 @@ static gboolean dlt_cb(const gchar *extcap _U_, const gchar *ifname _U_, gchar *
         g_free(caps);
     }
 
-    dlt_iter = dlts;
-    while (dlt_iter != NULL ) {
-        next = dlt_iter->next_dlt;
-        extcap_free_dlt(dlt_iter);
-        dlt_iter = next;
-    }
+    g_list_foreach(temp, extcap_free_dlt, NULL);
 
     return FALSE;
 }
@@ -274,27 +282,37 @@ extcap_get_if_dlts(const gchar *ifname, char **err_str) {
     return caps;
 }
 
+static void extcap_free_interface(gpointer i, gpointer user_data _U_) {
+
+    extcap_interface * interface = (extcap_interface *)i;
+
+    if ( i == NULL )
+        return;
+
+    g_free(interface->call);
+    g_free(interface->display);
+    g_free(interface->version);
+}
+
 static gboolean interfaces_cb(const gchar *extcap, const gchar *ifname _U_, gchar *output, void *data,
         char **err_str _U_) {
     GList **il = (GList **) data;
-    extcap_token_sentence *tokens;
-    extcap_interface *interfaces, *int_iter; /*, *next; */
-    if_info_t *if_info;
+    GList *interfaces = NULL, *walker = NULL;
+    extcap_interface *int_iter = NULL;
+    if_info_t *if_info = NULL;
 
-    tokens = extcap_tokenize_sentences(output);
-    extcap_parse_interfaces(tokens, &interfaces);
-
-    extcap_free_tokenized_sentence_list(tokens);
+    interfaces = extcap_parse_interfaces(output);
 
     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Extcap pipe %s ", extcap);
 
-    int_iter = interfaces;
-    while (int_iter != NULL ) {
+    walker = interfaces;
+    while (walker != NULL ) {
+        int_iter = (extcap_interface *)walker->data;
         if ( int_iter->if_type == EXTCAP_SENTENCE_INTERFACE && extcap_if_exists(int_iter->call) )
         {
             g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_WARNING, "Extcap interface \"%s\" is already provided by \"%s\" ",
                     int_iter->call, (gchar *)extcap_if_executable(int_iter->call) );
-            int_iter = int_iter->next_interface;
+            walker = g_list_next(walker);
             continue;
         }
 
@@ -323,9 +341,10 @@ static gboolean interfaces_cb(const gchar *extcap, const gchar *ifname _U_, gcha
          * interfaces) are handled internally */
         extcap_tool_add(extcap, int_iter);
 
-        int_iter = int_iter->next_interface;
+        walker = g_list_next(walker);
     }
-    extcap_free_interface(interfaces);
+
+    g_list_foreach(interfaces, extcap_free_interface, NULL);
 
     return TRUE;
 }
@@ -480,18 +499,28 @@ struct preference *
 extcap_pref_for_argument(const gchar *ifname, struct _extcap_arg * arg) {
     struct preference * pref = NULL;
 
-    GRegex * regex = g_regex_new ("[-]+", (GRegexCompileFlags) 0, (GRegexMatchFlags) 0, NULL );
-    if (regex) {
+    GRegex * regex_name = g_regex_new ("[-]+", (GRegexCompileFlags) 0, (GRegexMatchFlags) 0, NULL );
+    GRegex * regex_ifname = g_regex_new ("(?![a-zA-Z1-9_]).", (GRegexCompileFlags) 0, (GRegexMatchFlags) 0, NULL );
+    if (regex_name && regex_ifname) {
         if ( prefs_find_module("extcap") ) {
-            gchar * pref_name = g_regex_replace(regex, arg->call, strlen(arg->call), 0, "", (GRegexMatchFlags) 0, NULL );
-            gchar * pref_ifname = g_strdup(g_strconcat(ifname, ".", pref_name, NULL));
+            gchar * pref_name = g_regex_replace(regex_name, arg->call, strlen(arg->call), 0, "", (GRegexMatchFlags) 0, NULL );
+            gchar * ifname_underscore = g_regex_replace(regex_ifname, ifname, strlen(ifname), 0, "_", (GRegexMatchFlags) 0, NULL );
+            gchar * ifname_lowercase = g_ascii_strdown(ifname_underscore, -1);
+            gchar * pref_ifname = g_strconcat(ifname_lowercase, ".", pref_name, NULL);
 
             pref = prefs_find_preference(prefs_find_module("extcap"), pref_ifname);
 
             g_free(pref_name);
+            g_free(ifname_underscore);
+            g_free(ifname_lowercase);
             g_free(pref_ifname);
         }
-        g_regex_unref(regex);
+    }
+    if (regex_name) {
+        g_regex_unref(regex_name);
+    }
+    if (regex_ifname) {
+        g_regex_unref(regex_ifname);
     }
 
     return pref;
@@ -499,27 +528,20 @@ extcap_pref_for_argument(const gchar *ifname, struct _extcap_arg * arg) {
 
 static gboolean search_cb(const gchar *extcap _U_, const gchar *ifname _U_, gchar *output, void *data,
         char **err_str _U_) {
-    extcap_token_sentence *tokens = NULL;
     GList *arguments = NULL;
     GList **il = (GList **) data;
     module_t * dev_module = NULL;
 
-    tokens = extcap_tokenize_sentences(output);
-    arguments = extcap_parse_args(tokens);
-
-    extcap_free_tokenized_sentence_list(tokens);
-
-#if ARG_DEBUG
-    extcap_debug_arguments ( arguments );
-#endif
+    arguments = extcap_parse_args(output);
 
     dev_module = prefs_find_module("extcap");
 
     if ( dev_module ) {
         GList * walker = arguments;
 
-        GRegex * regex = g_regex_new ("[-]+", (GRegexCompileFlags) 0, (GRegexMatchFlags) 0, NULL );
-        if (regex) {
+        GRegex * regex_name = g_regex_new ("[-]+", (GRegexCompileFlags) 0, (GRegexMatchFlags) 0, NULL );
+        GRegex * regex_ifname = g_regex_new ("(?![a-zA-Z1-9_]).", (GRegexCompileFlags) 0, (GRegexMatchFlags) 0, NULL );
+        if (regex_name && regex_ifname) {
             while ( walker != NULL ) {
                 extcap_arg * arg = (extcap_arg *)walker->data;
                 arg->device_name = g_strdup(ifname);
@@ -527,8 +549,10 @@ static gboolean search_cb(const gchar *extcap _U_, const gchar *ifname _U_, gcha
                 if ( arg->save ) {
                     struct preference * pref = NULL;
 
-                    gchar * pref_name = g_regex_replace(regex, arg->call, strlen(arg->call), 0, "", (GRegexMatchFlags) 0, NULL );
-                    gchar * pref_ifname = g_strdup(g_strconcat(ifname, ".", pref_name, NULL));
+                    gchar * pref_name = g_regex_replace(regex_name, arg->call, strlen(arg->call), 0, "", (GRegexMatchFlags) 0, NULL );
+                    gchar * ifname_underscore = g_regex_replace(regex_ifname, ifname, strlen(ifname), 0, "_", (GRegexMatchFlags) 0, NULL );
+                    gchar * ifname_lowercase = g_ascii_strdown(ifname_underscore, -1);
+                    gchar * pref_ifname = g_strconcat(ifname_lowercase, ".", pref_name, NULL);
 
                     if ( ( pref = prefs_find_preference(dev_module, pref_ifname) ) == NULL ) {
                         /* Set an initial value */
@@ -550,12 +574,19 @@ static gboolean search_cb(const gchar *extcap _U_, const gchar *ifname _U_, gcha
                     }
 
                     g_free(pref_name);
+                    g_free(ifname_underscore);
+                    g_free(ifname_lowercase);
                     g_free(pref_ifname);
                 }
 
                 walker = g_list_next(walker);
             }
-            g_regex_unref(regex);
+        }
+        if (regex_name) {
+            g_regex_unref(regex_name);
+        }
+        if (regex_ifname) {
+            g_regex_unref(regex_ifname);
         }
     }
 
@@ -643,9 +674,44 @@ extcap_has_configuration(const char * ifname, gboolean is_required) {
     return found;
 }
 
-void extcap_if_cleanup(capture_options * capture_opts) {
+/* taken from capchild/capture_sync.c */
+static gboolean pipe_data_available(int pipe_fd) {
+#ifdef _WIN32 /* PeekNamedPipe */
+    HANDLE hPipe = (HANDLE) _get_osfhandle(pipe_fd);
+    DWORD bytes_avail;
+
+    if (hPipe == INVALID_HANDLE_VALUE)
+        return FALSE;
+
+    if (! PeekNamedPipe(hPipe, NULL, 0, NULL, &bytes_avail, NULL))
+        return FALSE;
+
+    if (bytes_avail > 0)
+        return TRUE;
+    return FALSE;
+#else /* select */
+    fd_set rfds;
+    struct timeval timeout;
+
+    FD_ZERO(&rfds);
+    FD_SET(pipe_fd, &rfds);
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 0;
+
+    if (select(pipe_fd+1, &rfds, NULL, NULL, &timeout) > 0)
+        return TRUE;
+
+    return FALSE;
+#endif
+}
+
+void extcap_if_cleanup(capture_options * capture_opts, gchar ** errormsg) {
     interface_options interface_opts;
+    extcap_userdata * userdata;
     guint icnt = 0;
+    gboolean overwrite_exitcode;
+    gchar * buffer;
+#define STDERR_BUFFER_SIZE 1024
 
     for (icnt = 0; icnt < capture_opts->ifaces->len; icnt++) {
         interface_opts = g_array_index(capture_opts->ifaces, interface_options,
@@ -655,6 +721,8 @@ void extcap_if_cleanup(capture_options * capture_opts) {
         if (interface_opts.if_type != IF_EXTCAP)
             continue;
 
+        overwrite_exitcode = FALSE;
+
         g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
                 "Extcap [%s] - Cleaning up fifo: %s; PID: %d", interface_opts.name,
                 interface_opts.extcap_fifo, interface_opts.extcap_pid);
@@ -681,6 +749,66 @@ void extcap_if_cleanup(capture_options * capture_opts) {
                 "Extcap [%s] - Closing spawned PID: %d", interface_opts.name,
                 interface_opts.extcap_pid);
 
+        userdata = (extcap_userdata *) interface_opts.extcap_userdata;
+        if ( userdata )
+        {
+            if (userdata->extcap_stderr_rd > 0 && pipe_data_available(userdata->extcap_stderr_rd) )
+            {
+                buffer = (gchar * )g_malloc0(sizeof(gchar) * STDERR_BUFFER_SIZE + 1);
+#ifdef _WIN32
+                win32_readfrompipe((HANDLE)_get_osfhandle(userdata->extcap_stderr_rd), STDERR_BUFFER_SIZE, buffer);
+#else
+                if (read(userdata->extcap_stderr_rd, buffer, sizeof(gchar) * STDERR_BUFFER_SIZE) <= 0 )
+                    buffer[0] = '\0';
+#endif
+                if ( strlen ( buffer) > 0 )
+                {
+                    userdata->extcap_stderr = g_strdup_printf("%s", buffer);
+                    userdata->exitcode = 1;
+                }
+                g_free(buffer);
+            }
+
+#ifndef _WIN32
+            /* Final child watch may not have been called */
+            if ( interface_opts.extcap_child_watch != 0 )
+            {
+                extcap_child_watch_cb(userdata->pid, 0, capture_opts);
+                /* it will have changed in extcap_child_watch_cb */
+                interface_opts = g_array_index(capture_opts->ifaces, interface_options,
+                                icnt);
+            }
+#endif
+
+            if ( userdata->extcap_stderr != NULL )
+                overwrite_exitcode = TRUE;
+
+            if ( overwrite_exitcode || userdata->exitcode != 0 )
+            {
+                if ( userdata->extcap_stderr != 0 )
+                {
+                    if ( *errormsg == NULL )
+                        *errormsg = g_strdup_printf("Error by extcap pipe: %s", userdata->extcap_stderr);
+                    else
+                    {
+                        gchar * temp = g_strconcat ( *errormsg, "\nError by extcap pipe: " ,userdata->extcap_stderr, NULL );
+                        g_free(*errormsg);
+                        *errormsg = temp;
+                    }
+                    g_free (userdata->extcap_stderr );
+                }
+
+                userdata->extcap_stderr = NULL;
+                userdata->exitcode = 0;
+            }
+        }
+
+        if (interface_opts.extcap_child_watch > 0)
+        {
+            g_source_remove(interface_opts.extcap_child_watch);
+            interface_opts.extcap_child_watch = 0;
+        }
+
         if (interface_opts.extcap_pid != INVALID_EXTCAP_PID)
         {
 #ifdef _WIN32
@@ -688,6 +816,9 @@ void extcap_if_cleanup(capture_options * capture_opts) {
 #endif
             g_spawn_close_pid(interface_opts.extcap_pid);
             interface_opts.extcap_pid = INVALID_EXTCAP_PID;
+
+            g_free(interface_opts.extcap_userdata);
+            interface_opts.extcap_userdata = NULL;
         }
 
         /* Make sure modified interface_opts is saved in capture_opts. */
@@ -713,11 +844,15 @@ extcap_add_arg_and_remove_cb(gpointer key, gpointer value, gpointer data) {
     return FALSE;
 }
 
-static void extcap_child_watch_cb(GPid pid, gint status _U_, gpointer user_data)
+void extcap_child_watch_cb(GPid pid, gint status _U_, gpointer user_data)
 {
     guint i;
     interface_options interface_opts;
-    capture_options *capture_opts = (capture_options *)user_data;
+    extcap_userdata * userdata = NULL;
+    capture_options * capture_opts = (capture_options *)(user_data);
+
+    if ( capture_opts == NULL || capture_opts->ifaces == NULL || capture_opts->ifaces->len == 0 )
+        return;
 
     /* Close handle to child process. */
     g_spawn_close_pid(pid);
@@ -728,7 +863,26 @@ static void extcap_child_watch_cb(GPid pid, gint status _U_, gpointer user_data)
         interface_opts = g_array_index(capture_opts->ifaces, interface_options, i);
         if (interface_opts.extcap_pid == pid)
         {
-            interface_opts.extcap_pid = INVALID_EXTCAP_PID;
+            userdata = (extcap_userdata *)interface_opts.extcap_userdata;
+            if ( userdata != NULL )
+            {
+                interface_opts.extcap_pid = INVALID_EXTCAP_PID;
+                userdata->exitcode = 0;
+#ifndef _WIN32
+                if ( WIFEXITED(status) )
+                {
+                    if ( WEXITSTATUS(status) != 0 )
+                        userdata->exitcode = WEXITSTATUS(status);
+                }
+                else
+                    userdata->exitcode = G_SPAWN_ERROR_FAILED;
+#else
+                if (status != 0)
+                    userdata->exitcode = status;
+#endif
+                if ( status == 0 && userdata->extcap_stderr != NULL )
+                    userdata->exitcode = 1;
+            }
             g_source_remove(interface_opts.extcap_child_watch);
             interface_opts.extcap_child_watch = 0;
 
@@ -743,10 +897,6 @@ static
 GPtrArray * extcap_prepare_arguments(interface_options interface_opts)
 {
     GPtrArray *result = NULL;
-#if ARG_DEBUG
-    gchar **tmp;
-    int tmp_i;
-#endif
 
     if (interface_opts.if_type == IF_EXTCAP )
     {
@@ -824,14 +974,6 @@ GPtrArray * extcap_prepare_arguments(interface_options interface_opts)
         add_arg(NULL);
 #undef add_arg
 
-#if ARG_DEBUG
-        /* Dump commandline parameters sent to extcap. */
-        for (tmp = (gchar **)result->pdata, tmp_i = 0; *tmp && **tmp; ++tmp_i, ++tmp)
-        {
-            g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "argv[%d]: %s", tmp_i, *tmp);
-        }
-#endif
-
     }
 
     return result;
@@ -844,6 +986,7 @@ extcap_init_interfaces(capture_options *capture_opts)
 {
     guint i;
     interface_options interface_opts;
+    extcap_userdata * userdata;
 
     for (i = 0; i < capture_opts->ifaces->len; i++)
     {
@@ -863,22 +1006,23 @@ extcap_init_interfaces(capture_options *capture_opts)
         /* Create extcap call */
         args = extcap_prepare_arguments(interface_opts);
 
-        interface_opts.extcap_userdata = NULL;
+        userdata = g_new0(extcap_userdata, 1);
 
-        pid = extcap_spawn_async(&interface_opts, args );
+        pid = extcap_spawn_async(userdata, args );
 
         g_ptr_array_foreach(args, (GFunc)g_free, NULL);
         g_ptr_array_free(args, TRUE);
 
         if ( pid == INVALID_EXTCAP_PID )
+        {
+            g_free(userdata);
             continue;
+        }
 
         interface_opts.extcap_pid = pid;
 
         interface_opts.extcap_child_watch =
             g_child_watch_add(pid, extcap_child_watch_cb, (gpointer)capture_opts);
-        capture_opts->ifaces = g_array_remove_index(capture_opts->ifaces, i);
-        g_array_insert_val(capture_opts->ifaces, i, interface_opts);
 
 #ifdef _WIN32
         /* On Windows, wait for extcap to connect to named pipe.
@@ -894,7 +1038,13 @@ extcap_init_interfaces(capture_options *capture_opts)
         {
             extcap_wait_for_pipe(pipe_h, pid);
         }
+
 #endif
+
+        interface_opts.extcap_userdata = (gpointer) userdata;
+
+        capture_opts->ifaces = g_array_remove_index(capture_opts->ifaces, i);
+        g_array_insert_val(capture_opts->ifaces, i, interface_opts);
     }
 
     return TRUE;
@@ -970,77 +1120,6 @@ gboolean extcap_create_pipe(char ** fifo)
     return TRUE;
 }
 
-#if ARG_DEBUG
-void extcap_debug_arguments ( extcap_arg *arg_iter )
-{
-    extcap_value *v = NULL;
-    GList *walker = NULL;
-
-    printf("debug - parser dump\n");
-    while (arg_iter != NULL) {
-        printf("ARG %d call=%s display=\"%s\" type=", arg_iter->arg_num, arg_iter->call, arg_iter->display);
-
-        switch (arg_iter->arg_type) {
-            case EXTCAP_ARG_INTEGER:
-            printf("int\n");
-            break;
-            case EXTCAP_ARG_UNSIGNED:
-            printf("unsigned\n");
-            break;
-            case EXTCAP_ARG_LONG:
-            printf("long\n");
-            break;
-            case EXTCAP_ARG_DOUBLE:
-            printf("double\n");
-            break;
-            case EXTCAP_ARG_BOOLEAN:
-            printf("boolean\n");
-            break;
-            case EXTCAP_ARG_MENU:
-            printf("menu\n");
-            break;
-            case EXTCAP_ARG_RADIO:
-            printf("radio\n");
-            break;
-            case EXTCAP_ARG_SELECTOR:
-            printf("selctor\n");
-            break;
-            case EXTCAP_ARG_STRING:
-            printf ( "string\n" );
-            break;
-            case EXTCAP_ARG_PASSWORD:
-            printf ( "PASSWORD\n" );
-            break;
-            case EXTCAP_ARG_MULTICHECK:
-            printf ( "unknown\n" );
-            break;
-            case EXTCAP_ARG_UNKNOWN:
-            printf ( "unknown\n" );
-            break;
-        }
-
-        if (arg_iter->range_start != NULL && arg_iter->range_end != NULL) {
-            printf("\tRange: ");
-            extcap_printf_complex(arg_iter->range_start);
-            printf(" - ");
-            extcap_printf_complex(arg_iter->range_end);
-            printf("\n");
-        }
-
-        for ( walker = g_list_first ( arg_iter->value_list ); walker; walker = walker->next )
-        {
-            v = (extcap_value *)walker->data;
-            if (v->is_default)
-            printf("*");
-            printf("\tcall=\"%p\" display=\"%p\"\n", v->call, v->display);
-            printf("\tcall=\"%s\" display=\"%s\"\n", v->call, v->display);
-        }
-
-        arg_iter = arg_iter->next_arg;
-    }
-}
-#endif
-
 /*
  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
  *