Fix for bug 1526. Compare to uppercased strings.
[obnox/wireshark/wip.git] / capture_sync.c
index ed3ad16576cfacf841aa633363e71045a8db2298..4b67d0cd58759cd38ca017bcaea377a35b6ae192 100644 (file)
@@ -1,10 +1,10 @@
 /* capture_sync.c
- * Synchronisation between Ethereal capture parent and child instances
+ * Synchronisation between Wireshark capture parent and child instances
  *
  * $Id$
  *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@ethereal.com>
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1998 Gerald Combs
  *
  * This program is free software; you can redistribute it and/or
@@ -28,8 +28,6 @@
 
 #ifdef HAVE_LIBPCAP
 
-#include <pcap.h>
-
 #include <glib.h>
 #include <stdio.h>
 #include <ctype.h>
 
 #include <signal.h>
 
+#ifdef _WIN32
+#include <fcntl.h>
+#include "epan/unicode-utils.h"
+#endif
+
 #ifdef HAVE_SYS_WAIT_H
 # include <sys/wait.h>
 #endif
 
+#include "capture-pcap-util.h"
+
 #ifndef _WIN32
 /*
  * Define various POSIX macros (and, in the case of WCOREDUMP, non-POSIX
 
 #include "globals.h"
 #include "file.h"
+#include <epan/filesystem.h>
 
 #include "capture.h"
 #include "capture_sync.h"
 #include "simple_dialog.h"
 
+#include "sync_pipe.h"
+
 #ifdef _WIN32
 #include "capture-wpcap.h"
 #endif
 #endif
 
 
+
 #ifndef _WIN32
 static const char *sync_pipe_signame(int);
 #endif
@@ -103,174 +112,10 @@ static const char *sync_pipe_signame(int);
 static gboolean sync_pipe_input_cb(gint source, gpointer user_data);
 static void sync_pipe_wait_for_child(capture_options *capture_opts);
 
-/*
- * Maximum length of sync pipe message data.  Must be < 2^24, as the
- * message length is 3 bytes.
- * XXX - this must be large enough to handle a Really Big Filter
- * Expression, as the error message for an incorrect filter expression
- * is a bit larger than the filter expression.
- */
-#define SP_MAX_MSG_LEN 4096
-
-
- /* write a message to the recipient pipe in the standard format 
-   (3 digit message length (excluding length and indicator field), 
-   1 byte message indicator and the rest is the message) */
-static void
-pipe_write_block(int pipe, char indicator, int len, const char *msg)
-{
-    guchar header[3+1]; /* indicator + 3-byte len */
-    int ret;
-
-    /*g_warning("write %d enter", pipe);*/
-
-    g_assert(indicator < '0' || indicator > '9');
-    g_assert(len <= SP_MAX_MSG_LEN);
-
-    /* write header (indicator + 3-byte len) */
-    header[0] = indicator;
-    header[1] = (len >> 16) & 0xFF;
-    header[2] = (len >> 8) & 0xFF;
-    header[3] = (len >> 0) & 0xFF;
-
-    ret = write(pipe, header, sizeof header);
-    if(ret == -1) {
-        return;
-    }
-
-    /* write value (if we have one) */
-    if(len) {
-        /*g_warning("write %d indicator: %c value len: %u msg: %s", pipe, indicator, len, msg);*/
-        ret = write(pipe, msg, len);
-        if(ret == -1) {
-            return;
-        }
-    } else {
-        /*g_warning("write %d indicator: %c no value", pipe, indicator);*/
-    }
-
-    /*g_warning("write %d leave", pipe);*/
-}
-
-
-/* read a message from the sending pipe in the standard format 
-   (1-byte message indicator, 3-byte message length (excluding length
-   and indicator field), and the rest is the message) */
-static int
-pipe_read_block(int pipe, char *indicator, int len, char *msg) {
-    int required;
-    int newly;
-    guchar header[4];
-    int offset;
-
-
-    /* read header (indicator and 3-byte length) */
-    required = 4;
-    offset = 0;
-    while(required) {
-        newly = read(pipe, &header[offset], required);
-        if (newly == 0) {
-            /* EOF */
-            /*g_warning("read %d header empty (capture closed)", pipe);*/
-            return newly;
-        }
-        if (newly < 0) {
-            /* error */
-            /*g_warning("read %d header error: %s", pipe, strerror(errno));*/
-            return newly;
-        }
-
-        required -= newly;
-        offset += newly;
-    }
-
-    /* convert header values */
-    *indicator = header[0];
-    required = header[1]<<16 | header[2]<<8 | header[3];
-
-    /* only indicator with no value? */
-    if(required == 0) {
-        /*g_warning("read %d indicator: %c empty value", pipe, *indicator);*/
-        return 4;
-    }
-
-    g_assert(required <= len);
-    len = required;
-
-    /* read value */
-    offset = 0;
-    while(required) {
-        newly = read(pipe, &msg[offset], required);
-        if (newly == -1) {
-            /* error */
-            /*g_warning("read %d value error, indicator: %u", pipe, *indicator);*/
-            return newly;
-        }
-
-        required -= newly;
-        offset += newly;
-    }
-
-    /*g_warning("read %d ok indicator: %c len: %u msg: %s", pipe, *indicator, len, msg);*/
-    return len + 4;
-}
-
-void
-sync_pipe_packet_count_to_parent(int packet_count)
-{
-    char tmp[SP_DECISIZE+1+1];
-
-    g_snprintf(tmp, sizeof(tmp), "%d", packet_count);
-
-    g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "sync_pipe_packet_count_to_parent: %s", tmp);
-
-    pipe_write_block(1, SP_PACKET_COUNT, strlen(tmp)+1, tmp);
-}
-
-void
-sync_pipe_filename_to_parent(const char *filename)
-{
-    g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "sync_pipe_filename_to_parent: %s", filename);
-
-    pipe_write_block(1, SP_FILE, strlen(filename)+1, filename);
-}
-
-void
-sync_pipe_errmsg_to_parent(const char *errmsg)
-{
-    g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "sync_pipe_errmsg_to_parent: %s", errmsg);
-
-    pipe_write_block(1, SP_ERROR_MSG, strlen(errmsg)+1, errmsg);
-}
-
-void
-sync_pipe_drops_to_parent(int drops)
-{
-    char tmp[SP_DECISIZE+1+1];
-
-
-    g_snprintf(tmp, sizeof(tmp), "%d", drops);
-
-    g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "sync_pipe_drops_to_parent: %s", tmp);
-
-    pipe_write_block(1, SP_DROPS, strlen(tmp)+1, tmp);
-}
-
-
-#ifdef _WIN32
-
-static void
-signal_pipe_capquit_to_child(capture_options *capture_opts)
-{
-
-    g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "signal_pipe_capquit_to_child");
-
-    pipe_write_block(capture_opts->signal_pipe_fd, SP_QUIT, 0, NULL);
-}
-#endif
 
 
-/* Add a string pointer to a NULL-terminated array of string pointers. */
+/* Append an arg (realloc) to an argc/argv array */
+/* (add a string pointer to a NULL-terminated array of string pointers) */
 static const char **
 sync_pipe_add_arg(const char **args, int *argc, const char *arg)
 {
@@ -296,8 +141,78 @@ sync_pipe_add_arg(const char **args, int *argc, const char *arg)
 
 
 
+#ifdef _WIN32
+/* Quote the argument element if necessary, so that it will get
+ * reconstructed correctly in the C runtime startup code.  Note that
+ * the unquoting algorithm in the C runtime is really weird, and
+ * rather different than what Unix shells do. See stdargv.c in the C
+ * runtime sources (in the Platform SDK, in src/crt).
+ *
+ * Stolen from GLib's protect_argv(), an internal routine that quotes
+ * string in an argument list so that they arguments will be handled
+ * correctly in the command-line string passed to CreateProcess()
+ * if that string is constructed by gluing those strings together.
+ */
+static gchar *
+protect_arg (const gchar *argv)
+{
+    gchar *new_arg;
+    const gchar *p = argv;
+    gchar *q;
+    gint len = 0;
+    gboolean need_dblquotes = FALSE;
+
+    while (*p) {
+        if (*p == ' ' || *p == '\t')
+            need_dblquotes = TRUE;
+        else if (*p == '"')
+            len++;
+        else if (*p == '\\') {
+            const gchar *pp = p;
+
+            while (*pp && *pp == '\\')
+                pp++;
+            if (*pp == '"')
+                len++;
+       }
+        len++;
+        p++;
+    }
+
+    q = new_arg = g_malloc (len + need_dblquotes*2 + 1);
+    p = argv;
+
+    if (need_dblquotes)
+        *q++ = '"';
+
+    while (*p) {
+        if (*p == '"')
+            *q++ = '\\';
+        else if (*p == '\\') {
+            const gchar *pp = p;
+
+            while (*pp && *pp == '\\')
+                pp++;
+            if (*pp == '"')
+                *q++ = '\\';
+       }
+       *q++ = *p;
+       p++;
+    }
+
+    if (need_dblquotes)
+        *q++ = '"';
+    *q++ = '\0';
+
+    return new_arg;
+}
+#endif
+
+
+
 #define ARGV_NUMBER_LEN 24
 
+/* a new capture run: start a new dumpcap task and hand over parameters through command line */
 gboolean
 sync_pipe_start(capture_options *capture_opts) {
     char ssnap[ARGV_NUMBER_LEN];
@@ -310,19 +225,26 @@ sync_pipe_start(capture_options *capture_opts) {
     char sautostop_duration[ARGV_NUMBER_LEN];
 #ifdef _WIN32
     char buffer_size[ARGV_NUMBER_LEN];
-    char sync_pipe_fd[ARGV_NUMBER_LEN];
-    char signal_pipe_fd[ARGV_NUMBER_LEN];
-    char *fontstring;
-    char *filterstring;
-    char *savefilestring;
-    int signal_pipe[2];                     /* pipe used to send messages from parent to child (currently only stop) */
+    HANDLE sync_pipe_read;                  /* pipe used to send messages from child to parent */
+    HANDLE sync_pipe_write;                 /* pipe used to send messages from child to parent */
+    HANDLE signal_pipe_read;                /* pipe used to send messages from parent to child (currently only stop) */
+    HANDLE signal_pipe_write;               /* pipe used to send messages from parent to child (currently only stop) */
+    GString *args = g_string_sized_new(200);
+    gchar *quoted_arg;
+    SECURITY_ATTRIBUTES sa;
+    STARTUPINFO si;
+    PROCESS_INFORMATION pi;
+    int i;
 #else
     char errmsg[1024+1];
+    int sync_pipe[2];                       /* pipe used to send messages from child to parent */
+    enum PIPES { PIPE_READ, PIPE_WRITE };   /* Constants 0 and 1 for PIPE_READ and PIPE_WRITE */
 #endif
+    int sync_pipe_read_fd;
+    const char *progfile_dir;
+    char *exename;
     int argc;
     const char **argv;
-    enum PIPES { PIPE_READ, PIPE_WRITE };   /* Constants 0 and 1 for PIPE_READ and PIPE_WRITE */
-    int sync_pipe[2];                       /* pipe used to send messages from child to parent */
 
 
     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_start");
@@ -330,14 +252,24 @@ sync_pipe_start(capture_options *capture_opts) {
 
     capture_opts->fork_child = -1;
 
+    progfile_dir = get_progfile_dir();
+    if (progfile_dir == NULL) {
+      /* We don't know where to find dumpcap. */
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "We don't know where to find dumpcap.");
+      return FALSE;
+    }
+
     /* Allocate the string pointer array with enough space for the
        terminating NULL pointer. */
     argc = 0;
     argv = g_malloc(sizeof (char *));
     *argv = NULL;
 
-    /* Now add those arguments used on all platforms. */
-    argv = sync_pipe_add_arg(argv, &argc, CHILD_NAME);
+    /* take Wireshark's absolute program path and replace "Wireshark" with "dumpcap" */
+    exename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "dumpcap", progfile_dir);
+
+    /* Make that the first argument in the argument list (argv[0]). */
+    argv = sync_pipe_add_arg(argv, &argc, exename);
 
     argv = sync_pipe_add_arg(argv, &argc, "-i");
     argv = sync_pipe_add_arg(argv, &argc, capture_opts->iface);
@@ -351,9 +283,9 @@ sync_pipe_start(capture_options *capture_opts) {
     if (capture_opts->linktype != -1) {
       argv = sync_pipe_add_arg(argv, &argc, "-y");
 #ifdef HAVE_PCAP_DATALINK_VAL_TO_NAME
-      g_snprintf(ssnap, ARGV_NUMBER_LEN, "%s",pcap_datalink_val_to_name(capture_opts->linktype));
+      g_snprintf(ssnap, ARGV_NUMBER_LEN, "%s",linktype_val_to_name(capture_opts->linktype));
 #else
-      /* XXX - just treat it as a number */
+      /* we can't get the type name, just treat it as a number */
       g_snprintf(ssnap, ARGV_NUMBER_LEN, "%d",capture_opts->linktype);
 #endif
       argv = sync_pipe_add_arg(argv, &argc, ssnap);
@@ -403,17 +335,39 @@ sync_pipe_start(capture_options *capture_opts) {
       argv = sync_pipe_add_arg(argv, &argc, sautostop_duration);
     }
 
-    if (!capture_opts->show_info) {
-      argv = sync_pipe_add_arg(argv, &argc, "-H");
-    }
-
     if (!capture_opts->promisc_mode)
       argv = sync_pipe_add_arg(argv, &argc, "-p");
 
+    /* dumpcap should be running in capture child mode (hidden feature) */
+#ifndef DEBUG_CHILD
+    argv = sync_pipe_add_arg(argv, &argc, "-Z");
+#endif
+
 #ifdef _WIN32
+    argv = sync_pipe_add_arg(argv, &argc, "-B");
+    g_snprintf(buffer_size, ARGV_NUMBER_LEN, "%d",capture_opts->buffer_size);
+    argv = sync_pipe_add_arg(argv, &argc, buffer_size);
+#endif
+
+    if (capture_opts->cfilter != NULL && strlen(capture_opts->cfilter) != 0) {
+      argv = sync_pipe_add_arg(argv, &argc, "-f");
+      argv = sync_pipe_add_arg(argv, &argc, capture_opts->cfilter);
+    }
+
+    if(capture_opts->save_file) {
+      argv = sync_pipe_add_arg(argv, &argc, "-w");
+      argv = sync_pipe_add_arg(argv, &argc, capture_opts->save_file);
+    }
+
+#ifdef _WIN32
+    /* init SECURITY_ATTRIBUTES */
+    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+    sa.bInheritHandle = TRUE;
+    sa.lpSecurityDescriptor = NULL;
+
     /* Create a pipe for the child process */
     /* (inrease this value if you have trouble while fast capture file switches) */
-    if(_pipe(sync_pipe, 5120, O_BINARY) < 0) {
+    if (! CreatePipe(&sync_pipe_read, &sync_pipe_write, &sa, 5120)) {
       /* Couldn't create the pipe between parent and child. */
       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Couldn't create sync pipe: %s",
                         strerror(errno));
@@ -422,66 +376,65 @@ sync_pipe_start(capture_options *capture_opts) {
     }
 
     /* Create a pipe for the parent process */
-    if(_pipe(signal_pipe, 512, O_BINARY) < 0) {
+    if (! CreatePipe(&signal_pipe_read, &signal_pipe_write, &sa, 512)) {
       /* Couldn't create the signal pipe between parent and child. */
       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Couldn't create signal pipe: %s",
                         strerror(errno));
-      eth_close(sync_pipe[PIPE_READ]);
-      eth_close(sync_pipe[PIPE_WRITE]);
+      CloseHandle(sync_pipe_read);
+      CloseHandle(sync_pipe_write);
       g_free( (gpointer) argv);
       return FALSE;
     }
 
-    capture_opts->signal_pipe_fd = signal_pipe[PIPE_WRITE];
-
-    argv = sync_pipe_add_arg(argv, &argc, "-B");
-    g_snprintf(buffer_size, ARGV_NUMBER_LEN, "%d",capture_opts->buffer_size);
-    argv = sync_pipe_add_arg(argv, &argc, buffer_size);
-
-    /* Convert font name to a quote-encapsulated string and pass to child */
-    argv = sync_pipe_add_arg(argv, &argc, "-m");
-    fontstring = g_strdup_printf("\"%s\"", prefs.PREFS_GUI_FONT_NAME);
-    argv = sync_pipe_add_arg(argv, &argc, fontstring);
-
-    /* Convert sync pipe write handle to a string and pass to child */
-    argv = sync_pipe_add_arg(argv, &argc, "-Z");
-    g_snprintf(sync_pipe_fd, ARGV_NUMBER_LEN, "sync:%d",sync_pipe[PIPE_WRITE]);
-    argv = sync_pipe_add_arg(argv, &argc, sync_pipe_fd);
-
-    /* Convert signal pipe read handle to a string and pass to child */
-    argv = sync_pipe_add_arg(argv, &argc, "-Z");
-    g_snprintf(signal_pipe_fd, ARGV_NUMBER_LEN, "signal:%d",signal_pipe[PIPE_READ]);
-    argv = sync_pipe_add_arg(argv, &argc, signal_pipe_fd);
+    /* init STARTUPINFO */
+    memset(&si, 0, sizeof(si));
+    si.cb           = sizeof(si);
+#ifdef DEBUG_CHILD
+    si.dwFlags = STARTF_USESHOWWINDOW;
+    si.wShowWindow  = SW_SHOW;
+#else
+    si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
+    si.wShowWindow  = SW_HIDE;  /* this hides the console window */
+    si.hStdInput = signal_pipe_read;
+    si.hStdOutput = sync_pipe_write;
+    si.hStdError = sync_pipe_write;
+    /*si.hStdError = (HANDLE) _get_osfhandle(2);*/
+#endif
 
-    /* Convert filter string to a quote delimited string and pass to child */
-    filterstring = NULL;
-    if (capture_opts->cfilter != NULL && strlen(capture_opts->cfilter) != 0) {
-      argv = sync_pipe_add_arg(argv, &argc, "-f");
-      filterstring = g_strdup_printf("\"%s\"", capture_opts->cfilter);
-      argv = sync_pipe_add_arg(argv, &argc, filterstring);
+    /* convert args array into a single string */
+    /* XXX - could change sync_pipe_add_arg() instead */
+    /* there is a drawback here: the length is internally limited to 1024 bytes */
+    for(i=0; argv[i] != 0; i++) {
+        if(i != 0) g_string_append_c(args, ' ');    /* don't prepend a space before the path!!! */
+        quoted_arg = protect_arg(argv[i]);
+        g_string_append(args, quoted_arg);
+        g_free(quoted_arg);
     }
 
-    /* Convert save file name to a quote delimited string and pass to child */
-    savefilestring = NULL;
-    if(capture_opts->save_file) {
-      argv = sync_pipe_add_arg(argv, &argc, "-w");
-      savefilestring = g_strdup_printf("\"%s\"", capture_opts->save_file);
-      argv = sync_pipe_add_arg(argv, &argc, savefilestring);
+    /* call dumpcap */
+    if(!CreateProcess(NULL, utf_8to16(args->str), NULL, NULL, TRUE,
+                      CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
+      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                    "Couldn't run %s in child process: error %u",
+                    args->str, GetLastError());
+      CloseHandle(sync_pipe_read);
+      CloseHandle(sync_pipe_write);
+      g_free( (gpointer) argv);
+      return FALSE;
     }
+    capture_opts->fork_child = (int) pi.hProcess;
+    g_string_free(args, TRUE);
 
-    /* Spawn process */
-    capture_opts->fork_child = spawnvp(_P_NOWAIT, ethereal_path, argv);
-    g_free(fontstring);
-    if (filterstring) {
-      g_free(filterstring);
-    }
-    if(savefilestring) {
-      g_free(savefilestring);
-    }
+    /* associate the operating system filehandle to a C run-time file handle */
+    /* (good file handle infos at: http://www.flounder.com/handles.htm) */
+    sync_pipe_read_fd = _open_osfhandle( (long) sync_pipe_read, _O_BINARY);
 
-    /* child own's the read side now, close our handle */
-    eth_close(signal_pipe[PIPE_READ]);
-#else
+    /* associate the operating system filehandle to a C run-time file handle */
+    capture_opts->signal_pipe_write_fd = _open_osfhandle( (long) signal_pipe_write, _O_BINARY);
+
+    /* child owns the read side now, close our handle */
+    CloseHandle(signal_pipe_read);
+#else /* _WIN32 */
     if (pipe(sync_pipe) < 0) {
       /* Couldn't create the pipe between parent and child. */
       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Couldn't create sync pipe: %s",
@@ -490,39 +443,18 @@ sync_pipe_start(capture_options *capture_opts) {
       return FALSE;
     }
 
-    argv = sync_pipe_add_arg(argv, &argc, "-m");
-    argv = sync_pipe_add_arg(argv, &argc, prefs.PREFS_GUI_FONT_NAME);
-
-    if (capture_opts->cfilter != NULL && capture_opts->cfilter != 0) {
-      argv = sync_pipe_add_arg(argv, &argc, "-f");
-      argv = sync_pipe_add_arg(argv, &argc, capture_opts->cfilter);
-    }
-
-    if(capture_opts->save_file) {
-      argv = sync_pipe_add_arg(argv, &argc, "-w");
-      argv = sync_pipe_add_arg(argv, &argc, capture_opts->save_file);
-    }
-
     if ((capture_opts->fork_child = fork()) == 0) {
       /*
-       * Child process - run Ethereal with the right arguments to make
-       * it just pop up the live capture dialog box and capture with
-       * the specified capture parameters, writing to the specified file.
-       *
-       * args: -i interface specification
-       * -w file to write
-       * -c count to capture
-       * -s snaplen
-       * -m / -b fonts
-       * -f "filter expression"
+       * Child process - run dumpcap with the right arguments to make
+       * it just capture with the specified capture parameters
        */
       eth_close(1);
       dup(sync_pipe[PIPE_WRITE]);
       eth_close(sync_pipe[PIPE_READ]);
-      execvp(ethereal_path, argv);
+      execv(exename, (gpointer)argv);
       g_snprintf(errmsg, sizeof errmsg, "Couldn't run %s in child process: %s",
-               ethereal_path, strerror(errno));
-      sync_pipe_errmsg_to_parent(errmsg);
+               exename, strerror(errno));
+      sync_pipe_errmsg_to_parent(errmsg, "");
 
       /* Exit with "_exit()", so that we don't close the connection
          to the X server (and cause stuff buffered up by our parent but
@@ -530,8 +462,12 @@ sync_pipe_start(capture_options *capture_opts) {
         our parent). */
       _exit(2);
     }
+
+    sync_pipe_read_fd = sync_pipe[PIPE_READ];
 #endif
 
+    g_free(exename);
+
     /* Parent process - read messages from the child process over the
        sync pipe. */
     g_free( (gpointer) argv);  /* free up arg array */
@@ -540,15 +476,19 @@ sync_pipe_start(capture_options *capture_opts) {
        open, and thus it completely closes, and thus returns to us
        an EOF indication, if the child closes it (either deliberately
        or by exiting abnormally). */
+#ifdef _WIN32
+    CloseHandle(sync_pipe_write);
+#else
     eth_close(sync_pipe[PIPE_WRITE]);
+#endif
 
     if (capture_opts->fork_child == -1) {
       /* We couldn't even create the child process. */
       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
                        "Couldn't create child process: %s", strerror(errno));
-      eth_close(sync_pipe[PIPE_READ]);
+      eth_close(sync_pipe_read_fd);
 #ifdef _WIN32
-      eth_close(signal_pipe[PIPE_WRITE]);
+      eth_close(capture_opts->signal_pipe_write_fd);
 #endif
       return FALSE;
     }
@@ -562,71 +502,198 @@ sync_pipe_start(capture_options *capture_opts) {
        the child process wants to tell us something. */
 
     /* we have a running capture, now wait for the real capture filename */
-    pipe_input_set_handler(sync_pipe[PIPE_READ], (gpointer) capture_opts, 
+    pipe_input_set_handler(sync_pipe_read_fd, (gpointer) capture_opts,
         &capture_opts->fork_child, sync_pipe_input_cb);
 
     return TRUE;
 }
 
+
+
+/* read a number of bytes from a pipe */
+/* (blocks until enough bytes read or an error occurs) */
+static int
+pipe_read_bytes(int pipe, char *bytes, int required) {
+    int newly;
+    int offset = 0;
+
+
+    while(required) {
+        newly = read(pipe, &bytes[offset], required);
+        if (newly == 0) {
+            /* EOF */
+            g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+                  "read from pipe %d: EOF (capture closed?)", pipe);
+            return offset;
+        }
+        if (newly < 0) {
+            /* error */
+            g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+                  "read from pipe %d: error(%u): %s", pipe, errno, strerror(errno));
+            return newly;
+        }
+
+        required -= newly;
+        offset += newly;
+    }
+
+    return offset;
+}
+
+/* convert header values (indicator and 4-byte length) */
+static void
+pipe_convert_header(const guchar *header, int header_len, char *indicator, int *block_len) {    
+
+    g_assert(header_len == 4);
+
+    /* convert header values */
+    *indicator = header[0];
+    *block_len = header[1]<<16 | header[2]<<8 | header[3];
+}
+
+/* read a message from the sending pipe in the standard format
+   (1-byte message indicator, 3-byte message length (excluding length
+   and indicator field), and the rest is the message) */
+static int
+pipe_read_block(int pipe, char *indicator, int len, char *msg) {
+    int required;
+    int newly;
+    guchar header[4];
+
+
+    /* read header (indicator and 3-byte length) */
+    newly = pipe_read_bytes(pipe, header, 4);
+    if(newly != 4) {
+        g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+              "read %d failed to read header: %u", pipe, newly);
+        return -1;
+    }
+
+    /* convert header values */
+    pipe_convert_header(header, 4, indicator, &required);
+
+    /* only indicator with no value? */
+    if(required == 0) {
+        g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+              "read %d indicator: %c empty value", pipe, *indicator);
+        return 4;
+    }
+
+    /* does the data fit into the given buffer? */
+    if(required > len) {
+        g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+              "read %d length error, required %d > len %d, indicator: %u",
+              pipe, required, len, *indicator);
+
+        /* we have a problem here, try to read some more bytes from the pipe to debug where the problem really is */
+        memcpy(msg, header, sizeof(header));
+        newly = read(pipe, &msg[sizeof(header)], len-sizeof(header));
+        g_warning("Unknown message from dumpcap, try to show it as a string: %s", msg);
+        return -1;
+    }
+    len = required;
+
+    /* read the actual block data */
+    newly = pipe_read_bytes(pipe, msg, required);
+    if(newly != required) {
+        g_warning("Unknown message from dumpcap, try to show it as a string: %s", msg);
+        return -1;
+    }
+
+    g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+          "read %d ok indicator: %c len: %u msg: %s", pipe, *indicator,
+          len, msg);
+    return newly + 4;
+}
+
+
 /* There's stuff to read from the sync pipe, meaning the child has sent
    us a message, or the sync pipe has closed, meaning the child has
    closed it (perhaps because it exited). */
-static gboolean 
+static gboolean
 sync_pipe_input_cb(gint source, gpointer user_data)
 {
   capture_options *capture_opts = (capture_options *)user_data;
   char buffer[SP_MAX_MSG_LEN+1];
   int  nread;
   char indicator;
+  int  primary_len;
+  char * primary_msg;
+  int  secondary_len;
+  char * secondary_msg;
 
 
   nread = pipe_read_block(source, &indicator, SP_MAX_MSG_LEN, buffer);
   if(nread <= 0) {
-    g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_input_cb: child has closed sync_pipe");
+    if (nread == 0)
+      g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+            "sync_pipe_input_cb: child has closed sync_pipe");
+    else
+      g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+            "sync_pipe_input_cb: error reading from sync pipe");
 
     /* The child has closed the sync pipe, meaning it's not going to be
        capturing any more packets.  Pick up its exit status, and
-       complain if it did anything other than exit with status 0. */
+       complain if it did anything other than exit with status 0.
+
+       We don't have to worry about killing the child, if the sync pipe 
+       returned an error. Usually this error is caused as the child killed itself 
+       while going down. Even in the rare cases that this isn't the case, 
+       the child will get an error when writing to the broken pipe the next time, 
+       cleaning itself up then. */
     sync_pipe_wait_for_child(capture_opts);
 
 #ifdef _WIN32
-    eth_close(capture_opts->signal_pipe_fd);
+    eth_close(capture_opts->signal_pipe_write_fd);
 #endif
     capture_input_closed(capture_opts);
     return FALSE;
   }
 
+  /* we got a valid message block from the child, process it */
   switch(indicator) {
   case SP_FILE:
-      if(!capture_input_new_file(capture_opts, buffer)) {
-        g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_input_cb: file failed, closing capture");
-
-        /* We weren't able to open the new capture file; user has been
-           alerted. Close the sync pipe. */
-        eth_close(source);
-
-        /* the child has send us a filename which we couldn't open.
-           this probably means, the child is creating files faster than we can handle it.
-           this should only be the case for very fast file switches
-           we can't do much more than telling the child to stop
-           (this is the "emergency brake" if user e.g. wants to switch files every second) */
-        sync_pipe_stop(capture_opts);
-      }
-      break;
+    if(!capture_input_new_file(capture_opts, buffer)) {
+      g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_input_cb: file failed, closing capture");
+
+      /* We weren't able to open the new capture file; user has been
+         alerted. Close the sync pipe. */
+      eth_close(source);
+
+      /* the child has send us a filename which we couldn't open.
+         this probably means, the child is creating files faster than we can handle it.
+         this should only be the case for very fast file switches
+         we can't do much more than telling the child to stop
+         (this is the "emergency brake" if user e.g. wants to switch files every second) */
+      sync_pipe_stop(capture_opts);
+    }
+    break;
   case SP_PACKET_COUNT:
     nread = atoi(buffer);
     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_input_cb: new packets %u", nread);
     capture_input_new_packets(capture_opts, nread);
     break;
   case SP_ERROR_MSG:
-    capture_input_error_message(capture_opts, buffer);
+    /* convert primary message */
+    pipe_convert_header(buffer, 4, &indicator, &primary_len);
+    primary_msg = buffer+4;
+    /* convert secondary message */
+    pipe_convert_header(primary_msg + primary_len, 4, &indicator, &secondary_len);
+    secondary_msg = primary_msg + primary_len + 4;
+    /* message output */
+    capture_input_error_message(capture_opts, primary_msg, secondary_msg);
+    /* the capture child will close the sync_pipe, nothing to do for now */
+    /* (an error message doesn't mean we have to stop capturing) */
+    break;
+  case SP_BAD_FILTER:
+    capture_input_cfilter_error_message(capture_opts, buffer);
     /* the capture child will close the sync_pipe, nothing to do for now */
     break;
   case SP_DROPS:
     capture_input_drops(capture_opts, atoi(buffer));
     break;
   default:
-      g_assert_not_reached();
+    g_assert_not_reached();
   }
 
   return TRUE;
@@ -645,12 +712,9 @@ sync_pipe_wait_for_child(capture_options *capture_opts)
   g_assert(capture_opts->fork_child != -1);
 
 #ifdef _WIN32
-  /* XXX - analyze the wait status and display more information
-     in the dialog box?
-     XXX - set "fork_child" to -1 if we find it exited? */
   if (_cwait(&wstatus, capture_opts->fork_child, _WAIT_CHILD) == -1) {
     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
-               "Child capture process stopped unexpectedly");
+               "Child capture process stopped unexpectedly (errno:%u)", errno);
   }
 #else
   if (wait(&wstatus) != -1) {
@@ -659,7 +723,7 @@ sync_pipe_wait_for_child(capture_options *capture_opts)
       /* the child will inform us about errors through the sync_pipe, which will popup */
       /* an error message, so don't popup another one */
 
-      /* XXX - if there are situations where the child won't send us such an error message, */
+      /* If there are situations where the child won't send us such an error message, */
       /* this should be fixed in the child and not here! */
       if (WEXITSTATUS(wstatus) != 0 && WEXITSTATUS(wstatus) != 1) {
         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
@@ -684,10 +748,10 @@ sync_pipe_wait_for_child(capture_options *capture_opts)
                    "Child capture process died: wait status %#o", wstatus);
     }
   }
+#endif
 
   /* No more child process. */
   capture_opts->fork_child = -1;
-#endif
 
   g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_wait_for_child: capture child closed");
 }
@@ -780,7 +844,7 @@ sync_pipe_signame(int sig)
     break;
 
   default:
-       /* XXX - returning a static buffer is ok in the context we use it here */
+       /* Returning a static buffer is ok in the context we use it here */
     g_snprintf(sigmsg_buf, sizeof sigmsg_buf, "Signal %d", sig);
     sigmsg = sigmsg_buf;
     break;
@@ -790,17 +854,39 @@ sync_pipe_signame(int sig)
 #endif
 
 
+#ifdef _WIN32
+/* tell the child through the signal pipe that we want to quit the capture */
+static void
+signal_pipe_capquit_to_child(capture_options *capture_opts)
+{
+    const char quit_msg[] = "QUIT";
+    int ret;
+
+
+    g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "signal_pipe_capquit_to_child");
+
+    /* it doesn't matter *what* we send here, the first byte will stop the capture */
+    /* simply sending a "QUIT" string */
+    /*pipe_write_block(capture_opts->signal_pipe_write_fd, SP_QUIT, quit_msg);*/
+    ret = write(capture_opts->signal_pipe_write_fd, quit_msg, sizeof quit_msg);
+    if(ret == -1) {
+        g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING,
+              "signal_pipe_capquit_to_child: %d header: error %s", capture_opts->signal_pipe_write_fd, strerror(errno));
+    }
+}
+#endif
+
+
 /* user wants to stop the capture run */
 void
 sync_pipe_stop(capture_options *capture_opts)
 {
-  /* XXX - in which cases this will be 0? */
-  if (capture_opts->fork_child != -1 && capture_opts->fork_child != 0) {
+  if (capture_opts->fork_child != -1) {
 #ifndef _WIN32
     /* send the SIGUSR1 signal to close the capture child gracefully. */
     kill(capture_opts->fork_child, SIGUSR1);
 #else
-    /* Win32 doesn't have the kill() system call, use the special signal pipe 
+    /* Win32 doesn't have the kill() system call, use the special signal pipe
        instead to close the capture child gracefully. */
     signal_pipe_capquit_to_child(capture_opts);
 #endif
@@ -808,27 +894,28 @@ sync_pipe_stop(capture_options *capture_opts)
 }
 
 
-/* Ethereal has to exit, force the capture child to close */
+/* Wireshark has to exit, force the capture child to close */
 void
 sync_pipe_kill(capture_options *capture_opts)
 {
-  /* XXX - in which cases this will be 0? */
-  if (capture_opts->fork_child != -1 && capture_opts->fork_child != 0) {
+  if (capture_opts->fork_child != -1) {
 #ifndef _WIN32
       kill(capture_opts->fork_child, SIGTERM); /* SIGTERM so it can clean up if necessary */
 #else
-      /* XXX: this is not the preferred method of closing a process!
+      /* Remark: This is not the preferred method of closing a process!
        * the clean way would be getting the process id of the child process,
        * then getting window handle hWnd of that process (using EnumChildWindows),
-       * and then do a SendMessage(hWnd, WM_CLOSE, 0, 0) 
+       * and then do a SendMessage(hWnd, WM_CLOSE, 0, 0)
        *
        * Unfortunately, I don't know how to get the process id from the
        * handle.  OpenProcess will get an handle (not a window handle)
        * from the process ID; it will not get a window handle from the
        * process ID.  (How could it?  A process can have more than one
-       * window.)
+       * window.  For that matter, a process might have *no* windows,
+       * as a process running dumpcap, the normal child process program,
+       * probably does.)
        *
-       * Hint: GenerateConsoleCtrlEvent() will only work if both processes are 
+       * Hint: GenerateConsoleCtrlEvent() will only work if both processes are
        * running in the same console; that's not necessarily the case for
        * us, as we might not be running in a console.
        * And this also will require to have the process id.