One more step in privilege separation.

Add a capture_interface_list(), which works similar to
get_interface_list() except that it forks dumpcap instead of calling
the pcap routines directly.  Use it in the GUI.

Add a "-I" flag to dumpcap, which prints out verbose interface
information.

Tested under Windows and Linux.

svn path=/trunk/; revision=22071
This commit is contained in:
Gerald Combs 2007-06-11 03:58:58 +00:00
parent f07a01dd51
commit e9f1a0b692
14 changed files with 567 additions and 147 deletions

View File

@ -69,9 +69,10 @@ typedef struct {
GList *get_interface_list(int *err, char **err_str);
/* Error values from "get_interface_list()". */
#define CANT_GET_INTERFACE_LIST 0 /* error getting list */
#define NO_INTERFACES_FOUND 1 /* list is empty */
/* Error values from "get_interface_list()/capture_interface_list()". */
#define CANT_GET_INTERFACE_LIST 1 /* error getting list */
#define NO_INTERFACES_FOUND 2 /* list is empty */
#define CANT_RUN_DUMPCAP 3 /* problem running 'dumpcap -I l' */
void free_interface_list(GList *if_list);

117
capture.c
View File

@ -40,6 +40,18 @@
#include <fcntl.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#include <signal.h>
#include <errno.h>
@ -47,6 +59,7 @@
#include <epan/packet.h>
#include <epan/dfilter/dfilter.h>
#include <epan/ws_strsplit.h>
#include "file.h"
#include "capture.h"
#include "capture_sync.h"
@ -142,7 +155,7 @@ capture_kill_child(capture_options *capture_opts)
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_INFO, "Capture Kill");
/* kill the capture child */
sync_pipe_kill(capture_opts);
sync_pipe_kill(capture_opts->fork_child);
}
@ -563,5 +576,107 @@ capture_input_closed(capture_options *capture_opts)
}
}
/**
* Fetch the interface list from a child process (dumpcap).
*
* @return A GList containing if_info_t structs if successful, NULL otherwise.
*/
/* XXX - We parse simple text output to get our interface list. Should
* we use "real" data serialization instead, e.g. via XML? */
GList *
capture_interface_list(int *err, char **err_str)
{
GList *if_list = NULL;
int i, j;
gchar *msg;
gchar **raw_list, **if_parts, **addr_parts;
gchar *name;
if_info_t *if_info;
if_addr_t *if_addr;
struct addrinfo *ai;
struct sockaddr_in *sa4;
struct sockaddr_in6 *sa6;
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List ...");
/* Try to get our interface list */
*err = sync_interface_list_open(&msg);
if(*err != 0) {
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List failed!");
if (*err_str)
*err_str = msg;
else
g_free(msg);
return NULL;
}
/* Split our lines */
raw_list = g_strsplit(msg, "\n", 0);
g_free(msg);
for (i = 0; raw_list[i] != NULL; i++) {
if_parts = g_strsplit(raw_list[i], "\t", 4);
if (if_parts[0] == NULL || if_parts[1] == NULL || if_parts[2] == NULL ||
if_parts[3] == NULL) {
g_strfreev(if_parts);
continue;
}
/* Number followed by the name, e.g "1. eth0" */
name = strchr(if_parts[0], ' ');
if (name) {
name++;
} else {
g_strfreev(if_parts);
continue;
}
if_info = g_malloc0(sizeof(if_info_t));
if_info->name = g_strdup(name);
if (strlen(if_parts[1]) > 0)
if_info->description = g_strdup(if_parts[1]);
addr_parts = g_strsplit(if_parts[2], ",", 0);
for (j = 0; addr_parts[j] != NULL; j++) {
/* XXX - We're failing to convert IPv6 addresses (on Ubuntu, at least) */
if (getaddrinfo(addr_parts[j], NULL, NULL, &ai) == 0) {
if_addr = NULL;
switch (ai->ai_family) {
case AF_INET:
if_addr = g_malloc0(sizeof(if_addr_t));
if_addr->type = AT_IPv4;
sa4 = (struct sockaddr_in *) ai->ai_addr;
if_addr->ip_addr.ip4_addr = sa4->sin_addr.s_addr;
break;
case AF_INET6:
if_addr = g_malloc0(sizeof(if_addr_t));
if_addr->type = AT_IPv6;
sa6 = (struct sockaddr_in6 *) ai->ai_addr;
memcpy(&if_addr->ip_addr.ip6_addr, sa6->sin6_addr.s6_addr, 16);
break;
}
if (if_addr) {
if_info->ip_addr = g_slist_append(if_info->ip_addr, if_addr);
}
freeaddrinfo(ai);
}
}
if (strcmp(if_parts[3], "loopback") == 0)
if_info->loopback = TRUE;
g_strfreev(if_parts);
g_strfreev(addr_parts);
if_list = g_list_append(if_list, if_info);
}
g_strfreev(raw_list);
/* Check to see if we built a list */
if (if_list == NULL) {
if (*err_str)
*err_str = g_strdup("No interfaces found");
*err = NO_INTERFACES_FOUND;
}
return if_list;
}
#endif /* HAVE_LIBPCAP */

View File

@ -81,5 +81,10 @@ extern void capture_input_cfilter_error_message(capture_options *capture_opts, c
*/
extern void capture_input_closed(capture_options *capture_opts);
/**
* Fetch the interface list from a child process.
*/
extern GList *capture_interface_list(int *err, char **err_str);
#endif /* capture.h */

View File

@ -31,6 +31,10 @@
#include <string.h>
#include <ctype.h>
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#include <glib.h>
#include <epan/packet.h>
@ -425,8 +429,9 @@ int capture_opts_list_link_layer_types(capture_options *capture_opts)
return 0;
}
int capture_opts_list_interfaces()
/* Return an ASCII-formatted list of interfaces. */
int
capture_opts_list_interfaces(gboolean verbose)
{
GList *if_list;
GList *if_entry;
@ -434,12 +439,11 @@ int capture_opts_list_interfaces()
int err;
gchar *err_str;
int i;
#if 0
GSList *ip_addr;
if_addr_t *if_addr;
guint8 ipv4[4];
#endif
char addr_str[NI_MAXHOST];
struct sockaddr_in sa4;
struct sockaddr_in6 sa6;
if_list = get_interface_list(&err, &err_str);
if (if_list == NULL) {
@ -453,7 +457,7 @@ int capture_opts_list_interfaces()
cmdarg_err("There are no interfaces on which a capture can be done");
break;
}
return 2;
return err;
}
i = 1; /* Interface id number */
@ -461,27 +465,61 @@ int capture_opts_list_interfaces()
if_entry = g_list_next(if_entry)) {
if_info = if_entry->data;
printf("%d. %s", i++, if_info->name);
if (!verbose) {
/* Add the description if it exists */
if (if_info->description != NULL)
printf(" (%s)", if_info->description);
#if 0
} else {
/*
* Add the contents of the if_entry struct in a parseable format.
* Each if_entry element is tab-separated. Addresses are comma-
* separated.
*/
/* XXX - Make sure our description doesn't contain a tab */
if (if_info->description != NULL)
printf("\t%s\t", if_info->description);
else
printf("\t\t");
for(ip_addr = g_slist_nth(if_info->ip_addr, 0); ip_addr != NULL;
ip_addr = g_slist_next(ip_addr)) {
if (ip_addr != g_slist_nth(if_info->ip_addr, 0))
printf(",");
if_addr = ip_addr->data;
switch(if_addr->type) {
case AT_IPv4:
memcpy(ipv4, (void *) &if_addr->ip_addr.ip4_addr, 4);
printf(" %u.%u.%u.%u", ipv4[0], ipv4[1], ipv4[2], ipv4[3]);
sa4.sin_family = AF_INET;
sa4.sin_addr.s_addr = if_addr->ip_addr.ip4_addr;
if (getnameinfo((struct sockaddr *) &sa4, sizeof(sa4),
addr_str, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0) {
printf(addr_str);
} else {
printf("<unknown IPv4>");
}
break;
case AT_IPv6:
/* XXX - display the IPv6 address without using stuff from epan */
printf(" XXX-IPv6");
sa6.sin6_family = AF_INET6;
memcpy(&sa6.sin6_addr.s6_addr, &if_addr->ip_addr.ip6_addr, 16);
if (getnameinfo((struct sockaddr *) &sa6, sizeof(sa6),
addr_str, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0) {
printf(addr_str);
} else {
printf("<unknown IPv6>");
}
break;
default:
printf(" unknown address type %u", if_addr->type);
printf("<type unknown %u>", if_addr->type);
}
}
#endif
if (if_info->loopback)
printf("\tloopback");
else
printf("\tnetwork");
}
printf("\n");
}
free_interface_list(if_list);

View File

@ -118,7 +118,7 @@ capture_opts_list_link_layer_types(capture_options *capture_opts);
/* list interfaces */
extern int
capture_opts_list_interfaces(void);
capture_opts_list_interfaces(gboolean verbose);
/* trim the snaplen entry */
extern void

View File

@ -508,6 +508,242 @@ sync_pipe_start(capture_options *capture_opts) {
return TRUE;
}
/*
* Get an interface list using dumpcap. On success, msg points to
* a buffer containing the dumpcap output and returns 0. On failure, msg
* points to the error message returned by dumpcap, and returns dumpcap's
* exit value. In either case, msg must be freed with g_free().
*/
/* XXX - This duplicates a lot of code in sync_pipe_start() and sync_interface_list_open() */
#define PIPE_BUF_SIZE 5120
int
sync_interface_list_open(gchar **msg) {
#ifdef _WIN32
HANDLE sync_pipe_read; /* pipe used to send messages from child to parent */
HANDLE sync_pipe_write; /* pipe used to send messages from parent to child */
GString *args = g_string_sized_new(200);
gchar *quoted_arg;
SECURITY_ATTRIBUTES sa;
STARTUPINFO si;
PROCESS_INFORMATION pi;
int i;
#else
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 fork_child = -1, fork_child_status;
int sync_pipe_read_fd = -1;
const char *progfile_dir;
char *exename;
int argc;
const char **argv;
GString *msg_buf = NULL;
gchar buf[PIPE_BUF_SIZE+1];
int count;
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_interface_list_open");
if (!msg) {
/* We can't return anything */
#ifdef _WIN32
g_string_free(args, TRUE);
#endif
return -1;
}
progfile_dir = get_progfile_dir();
if (progfile_dir == NULL) {
/* We don't know where to find dumpcap. */
*msg = g_strdup("We don't know where to find dumpcap.");
return CANT_RUN_DUMPCAP;
}
/* Allocate the string pointer array with enough space for the
terminating NULL pointer. */
argc = 0;
argv = g_malloc(sizeof (char *));
*argv = NULL;
/* 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);
/* Ask for the interface list */
argv = sync_pipe_add_arg(argv, &argc, "-I");
argv = sync_pipe_add_arg(argv, &argc, "l");
/* dumpcap should be running in capture child mode (hidden feature) */
#ifndef DEBUG_CHILD
argv = sync_pipe_add_arg(argv, &argc, "-Z");
#endif
#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 (! CreatePipe(&sync_pipe_read, &sync_pipe_write, &sa, 5120)) {
/* Couldn't create the pipe between parent and child. */
*msg = g_strdup_printf("Couldn't create sync pipe: %s", strerror(errno));
g_free( (gpointer) argv);
return CANT_RUN_DUMPCAP;
}
/* 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 = NULL;
si.hStdOutput = sync_pipe_write;
si.hStdError = sync_pipe_write;
/*si.hStdError = (HANDLE) _get_osfhandle(2);*/
#endif
/* 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);
}
/* call dumpcap */
if(!CreateProcess(NULL, utf_8to16(args->str), NULL, NULL, TRUE,
CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
*msg = g_strdup_printf("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 CANT_RUN_DUMPCAP;
}
fork_child = (int) pi.hProcess;
g_string_free(args, TRUE);
/* 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);
#else /* _WIN32 */
if (pipe(sync_pipe) < 0) {
/* Couldn't create the pipe between parent and child. */
*msg = g_strdup_printf("Couldn't create sync pipe: %s", strerror(errno));
g_free(argv);
return CANT_RUN_DUMPCAP;
}
if ((fork_child = fork()) == 0) {
/*
* 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]);
execv(exename, (gpointer)argv);
*msg = g_strdup_printf("Couldn't run %s in child process: %s",
exename, strerror(errno));
return CANT_RUN_DUMPCAP;
}
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 */
/* Close the write side of the pipe, so that only the child has it
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 (fork_child == -1) {
/* We couldn't even create the child process. */
*msg = g_strdup_printf("Couldn't create child process: %s", strerror(errno));
eth_close(sync_pipe_read_fd);
return CANT_RUN_DUMPCAP;
}
/* we might wait for a moment till child is ready, so update screen now */
main_window_update();
/* We were able to set up to read dumpcap's output. Do so and
return its exit value. */
msg_buf = g_string_new("");
while ((count = eth_read(sync_pipe_read_fd, buf, PIPE_BUF_SIZE)) > 0) {
buf[count] = '\0';
g_string_append(msg_buf, buf);
}
eth_close(sync_pipe_read_fd);
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_interface_list_open: wait till child closed");
#ifdef _WIN32
if (_cwait(&fork_child_status, fork_child, _WAIT_CHILD) == -1) {
g_string_free(msg_buf, TRUE);
*msg = g_strdup_printf("Child capture process stopped unexpectedly "
"(errno:%u)", errno);
return CANT_RUN_DUMPCAP;
}
#else
if (wait(&fork_child_status) != -1) {
if (WIFEXITED(fork_child_status)) {
/* The child exited. */
fork_child_status = WEXITSTATUS(fork_child_status);
} else {
g_string_free(msg_buf, TRUE);
if (WIFSTOPPED(fork_child_status)) {
/* It stopped, rather than exiting. "Should not happen." */
*msg = g_strdup_printf("Child capture process stopped: %s",
sync_pipe_signame(WSTOPSIG(fork_child_status)));
} else if (WIFSIGNALED(fork_child_status)) {
/* It died with a signal. */
*msg = g_strdup_printf("Child capture process died: %s%s",
sync_pipe_signame(WTERMSIG(fork_child_status)),
WCOREDUMP(fork_child_status) ? " - core dumped" : "");
} else {
/* What? It had to either have exited, or stopped, or died with
a signal; what happened here? */
*msg = g_strdup_printf("Child capture process died: wait status %#o",
fork_child_status);
}
return CANT_RUN_DUMPCAP;
}
} else {
g_string_free(msg_buf, TRUE);
*msg = g_strdup_printf("Child capture process stopped unexpectedly "
"(errno:%u)", errno);
return CANT_RUN_DUMPCAP;
}
#endif
*msg = msg_buf->str;
g_string_free(msg_buf, FALSE);
return fork_child_status;
}
/* read a number of bytes from a pipe */
@ -896,11 +1132,11 @@ sync_pipe_stop(capture_options *capture_opts)
/* Wireshark has to exit, force the capture child to close */
void
sync_pipe_kill(capture_options *capture_opts)
sync_pipe_kill(int fork_child)
{
if (capture_opts->fork_child != -1) {
if (fork_child != -1) {
#ifndef _WIN32
kill(capture_opts->fork_child, SIGTERM); /* SIGTERM so it can clean up if necessary */
kill(fork_child, SIGTERM); /* SIGTERM so it can clean up if necessary */
#else
/* Remark: This is not the preferred method of closing a process!
* the clean way would be getting the process id of the child process,
@ -920,7 +1156,7 @@ sync_pipe_kill(capture_options *capture_opts)
* us, as we might not be running in a console.
* And this also will require to have the process id.
*/
TerminateProcess((HANDLE) (capture_opts->fork_child), 0);
TerminateProcess((HANDLE) (fork_child), 0);
#endif
}
}

View File

@ -55,12 +55,17 @@ sync_pipe_stop(capture_options *capture_opts);
/** User wants to stop the program, just kill the child as soon as possible */
extern void
sync_pipe_kill(capture_options *capture_opts);
sync_pipe_kill(int fork_child);
/** does the parent signalled the child to stop */
/** Has the parent signalled the child to stop? */
#ifdef _WIN32
extern gboolean
signal_pipe_check_running(void);
#endif
/** Get an interface list using dumpcap */
extern int
sync_interface_list_open(gchar **msg);
#endif /* capture_sync.h */

View File

@ -14,6 +14,7 @@ S<[ B<-D> ]>
S<[ B<-f> E<lt>capture filterE<gt> ]>
S<[ B<-h> ]>
S<[ B<-i> E<lt>capture interfaceE<gt>|- ]>
S<[ B<-I> E<lt>l|sE<gt> ]>
S<[ B<-L> ]>
S<[ B<-p> ]>
S<[ B<-s> E<lt>capture snaplenE<gt> ]>
@ -155,6 +156,14 @@ standard libpcap format.
Note: the Win32 version of B<Dumpcap> doesn't support capturing from
pipes or stdin!
=item -I
If run with the B<l> argument, print a verbose, machine-readable interface
list, similar to the B<-D> flag.
If run with the B<s> argument, print statistics for each interface every
second until the program terminates.
=item -L
List the data link types supported by the interface and exit. The reported

View File

@ -115,6 +115,7 @@ print_usage(gboolean print_ver) {
fprintf(output, " -y <link type> link layer type (def: first appropriate)\n");
fprintf(output, " -D print list of interfaces and exit\n");
fprintf(output, " -L print list of link-layer types of iface and exit\n");
fprintf(output, " -I [l|s] print a detailed interface list (l) or interface statistics (s).\n");
fprintf(output, "\n");
fprintf(output, "Stop conditions:\n");
fprintf(output, " -c <packet count> stop after n packets (def: infinite)\n");
@ -161,7 +162,10 @@ cmdarg_err(const char *fmt, ...)
va_list ap;
if(capture_child) {
/* XXX - convert to g_log */
/* Print a bare error */
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
} else {
va_start(ap, fmt);
fprintf(stderr, "dumpcap: ");
@ -245,7 +249,7 @@ main(int argc, char *argv[])
gboolean list_link_layer_types = FALSE;
int status;
#define OPTSTRING_INIT "a:b:c:Df:hi:Lps:vw:y:Z"
#define OPTSTRING_INIT "a:b:c:Df:hI:i:Lps:vw:y:Z"
#ifdef _WIN32
#define OPTSTRING_WIN32 "B:"
@ -362,9 +366,15 @@ main(int argc, char *argv[])
/*** all non capture option specific ***/
case 'D': /* Print a list of capture devices and exit */
status = capture_opts_list_interfaces();
status = capture_opts_list_interfaces(FALSE);
exit_main(status);
break;
/* XXX - We might want to use 'D' for this. Do we use GNU
* getopt on every platform (which supports optional arguments)? */
/* XXX - Implement interface stats */
case 'I':
status = capture_opts_list_interfaces(TRUE);
exit_main(status);
case 'L': /* Print list of link-layer types and exit */
list_link_layer_types = TRUE;
break;

View File

@ -236,7 +236,7 @@ set_link_type_list(GtkWidget *linktype_om, GtkWidget *entry)
/*
* Try to get the list of known interfaces.
*/
if_list = get_interface_list(&err, NULL);
if_list = capture_interface_list(&err, NULL);
if (if_list != NULL) {
/*
* We have the list - check it.
@ -627,7 +627,7 @@ capture_prep_cb(GtkWidget *w _U_, gpointer d _U_)
}
#endif
if_list = get_interface_list(&err, &err_str);
if_list = capture_interface_list(&err, &err_str);
if (if_list == NULL && err == CANT_GET_INTERFACE_LIST) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str);
g_free(err_str);

View File

@ -458,7 +458,7 @@ capture_if_cb(GtkWidget *w _U_, gpointer d _U_)
#endif
/* LOAD THE INTERFACES */
if_list = get_interface_list(&err, &err_str);
if_list = capture_interface_list(&err, &err_str);
if_list = g_list_sort (if_list, if_list_comparator_alph);
if (if_list == NULL && err == CANT_GET_INTERFACE_LIST) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str);

View File

@ -43,6 +43,7 @@
#include "capture_ui_utils.h"
#include "main.h"
#include "compat_macros.h"
#include "capture.h"
#define DEVICE_KEY "device"
#define PROM_MODE_KEY "prom_mode"
@ -108,7 +109,7 @@ capture_prefs_show(void)
/*
* XXX - what if we can't get the list?
*/
if_list = get_interface_list(&err, NULL);
if_list = capture_interface_list(&err, NULL);
combo_list = build_capture_combo_list(if_list, FALSE);
free_interface_list(if_list);
if (combo_list != NULL) {
@ -696,7 +697,7 @@ ifopts_if_clist_add(void)
guint i;
guint nitems;
if_list = get_interface_list(&err, &err_str);
if_list = capture_interface_list(&err, &err_str);
if (if_list == NULL && err == CANT_GET_INTERFACE_LIST) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str);
g_free(err_str);

View File

@ -2568,7 +2568,7 @@ main(int argc, char *argv[])
/*** all non capture option specific ***/
case 'D': /* Print a list of capture devices and exit */
#ifdef HAVE_LIBPCAP
capture_opts_list_interfaces();
capture_opts_list_interfaces(FALSE);
exit(0);
#else
capture_option_specified = TRUE;

View File

@ -948,7 +948,7 @@ main(int argc, char *argv[])
break;
case 'D': /* Print a list of capture devices and exit */
#ifdef HAVE_LIBPCAP
status = capture_opts_list_interfaces();
status = capture_opts_list_interfaces(FALSE);
exit(status);
#else
capture_option_specified = TRUE;