Fix segmentation fault when NULL err_msg pointer passed to capture_interface_list...
[obnox/wireshark/wip.git] / capture.c
1 /* capture.c
2  * Routines for packet capture
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #ifdef HAVE_LIBPCAP
30
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34
35 #include <stdlib.h>
36 #include <string.h>
37 #include <ctype.h>
38
39 #ifdef HAVE_FCNTL_H
40 #include <fcntl.h>
41 #endif
42
43 #ifdef HAVE_SYS_TYPES_H
44 #include <sys/types.h>
45 #endif
46
47 #ifdef HAVE_SYS_SOCKET_H
48 #include <sys/socket.h>
49 #endif
50
51 #ifdef HAVE_NETDB_H
52 #include <netdb.h>
53 #endif
54
55 #include <signal.h>
56 #include <errno.h>
57
58 #include <glib.h>
59
60 #include <epan/packet.h>
61 #include <epan/dfilter/dfilter.h>
62 #include <epan/ws_strsplit.h>
63 #include "file.h"
64 #include "capture.h"
65 #include "capture_sync.h"
66 #include "capture_info.h"
67 #include "capture_ui_utils.h"
68 #include "util.h"
69 #include "capture-pcap-util.h"
70 #include "alert_box.h"
71 #include "simple_dialog.h"
72 #include <epan/prefs.h>
73 #include "conditions.h"
74 #include "ringbuffer.h"
75
76 #ifdef _WIN32
77 #include "capture-wpcap.h"
78 #endif
79 #include "ui_util.h"
80 #include "file_util.h"
81 #include "log.h"
82
83
84
85 /**
86  * Start a capture.
87  *
88  * @return TRUE if the capture starts successfully, FALSE otherwise.
89  */
90 gboolean
91 capture_start(capture_options *capture_opts)
92 {
93   gboolean ret;
94
95
96   /* close the currently loaded capture file */
97   cf_close(capture_opts->cf);
98
99   g_assert(capture_opts->state == CAPTURE_STOPPED);
100   capture_opts->state = CAPTURE_PREPARING;
101
102   g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Start ...");
103
104   /* try to start the capture child process */
105   ret = sync_pipe_start(capture_opts);
106   if(!ret) {
107       if(capture_opts->save_file != NULL) {
108           g_free(capture_opts->save_file);
109           capture_opts->save_file = NULL;
110       }
111
112       g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Start failed!");
113       capture_opts->state = CAPTURE_STOPPED;
114   } else {
115       /* the capture child might not respond shortly after bringing it up */
116       /* (especially it will block, if no input coming from an input capture pipe (e.g. mkfifo) is coming in) */
117
118       /* to prevent problems, bring the main GUI into "capture mode" right after successfully */
119       /* spawn/exec the capture child, without waiting for any response from it */
120       cf_callback_invoke(cf_cb_live_capture_prepared, capture_opts);
121
122       if(capture_opts->show_info)
123         capture_info_open(capture_opts->iface);
124   }
125
126   return ret;
127 }
128
129
130 void
131 capture_stop(capture_options *capture_opts)
132 {
133   g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Stop ...");
134
135   cf_callback_invoke(cf_cb_live_capture_stopping, capture_opts);
136
137   /* stop the capture child gracefully */
138   sync_pipe_stop(capture_opts);
139 }
140
141
142 void
143 capture_restart(capture_options *capture_opts)
144 {
145     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Restart");
146
147     capture_opts->restart = TRUE;
148     capture_stop(capture_opts);
149 }
150
151
152 void
153 capture_kill_child(capture_options *capture_opts)
154 {
155   g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_INFO, "Capture Kill");
156
157   /* kill the capture child */
158   sync_pipe_kill(capture_opts->fork_child);
159 }
160
161
162
163 /* We've succeeded a (non real-time) capture, try to read it into a new capture file */
164 static gboolean
165 capture_input_read_all(capture_options *capture_opts, gboolean is_tempfile, gboolean drops_known,
166 guint32 drops)
167 {
168   int err;
169
170
171   /* Capture succeeded; attempt to open the capture file. */
172   if (cf_open(capture_opts->cf, capture_opts->save_file, is_tempfile, &err) != CF_OK) {
173     /* We're not doing a capture any more, so we don't have a save
174        file. */
175     return FALSE;
176   }
177
178   /* Set the read filter to NULL. */
179   /* XXX - this is odd here, try to put it somewhere, where it fits better */
180   cf_set_rfcode(capture_opts->cf, NULL);
181
182   /* Get the packet-drop statistics.
183
184      XXX - there are currently no packet-drop statistics stored
185      in libpcap captures, and that's what we're reading.
186
187      At some point, we will add support in Wiretap to return
188      packet-drop statistics for capture file formats that store it,
189      and will make "cf_read()" get those statistics from Wiretap.
190      We clear the statistics (marking them as "not known") in
191      "cf_open()", and "cf_read()" will only fetch them and mark
192      them as known if Wiretap supplies them, so if we get the
193      statistics now, after calling "cf_open()" but before calling
194      "cf_read()", the values we store will be used by "cf_read()".
195
196      If a future libpcap capture file format stores the statistics,
197      we'll put them into the capture file that we write, and will
198      thus not have to set them here - "cf_read()" will get them from
199      the file and use them. */
200   if (drops_known) {
201     cf_set_drops_known(capture_opts->cf, TRUE);
202
203     /* XXX - on some systems, libpcap doesn't bother filling in
204        "ps_ifdrop" - it doesn't even set it to zero - so we don't
205        bother looking at it.
206
207        Ideally, libpcap would have an interface that gave us
208        several statistics - perhaps including various interface
209        error statistics - and would tell us which of them it
210        supplies, allowing us to display only the ones it does. */
211     cf_set_drops(capture_opts->cf, drops);
212   }
213
214   /* read in the packet data */
215   switch (cf_read(capture_opts->cf)) {
216
217   case CF_READ_OK:
218   case CF_READ_ERROR:
219     /* Just because we got an error, that doesn't mean we were unable
220        to read any of the file; we handle what we could get from the
221        file. */
222     break;
223
224   case CF_READ_ABORTED:
225     /* User wants to quit program. Exit by leaving the main loop,
226        so that any quit functions we registered get called. */
227     main_window_nested_quit();
228     return FALSE;
229   }
230
231   /* if we didn't captured even a single packet, close the file again */
232   if(cf_get_packet_count(capture_opts->cf) == 0 && !capture_opts->restart) {
233     simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
234 "%sNo packets captured!%s\n"
235 "\n"
236 "As no data was captured, closing the %scapture file!\n"
237 "\n"
238 "\n"
239 "Help about capturing can be found at:\n"
240 "\n"
241 "       http://wiki.wireshark.org/CaptureSetup"
242 #ifdef _WIN32
243 "\n\n"
244 "Wireless (Wi-Fi/WLAN):\n"
245 "Try to switch off promiscuous mode in the Capture Options!"
246 #endif
247 "",
248     simple_dialog_primary_start(), simple_dialog_primary_end(),
249     (cf_is_tempfile(capture_opts->cf)) ? "temporary " : "");
250     cf_close(capture_opts->cf);
251   }
252   return TRUE;
253 }
254
255
256 /* capture child tells us we have a new (or the first) capture file */
257 gboolean
258 capture_input_new_file(capture_options *capture_opts, gchar *new_file)
259 {
260   gboolean is_tempfile;
261   int  err;
262
263
264   if(capture_opts->state == CAPTURE_PREPARING) {
265     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture started!");
266   }
267   g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "File: \"%s\"", new_file);
268
269   g_assert(capture_opts->state == CAPTURE_PREPARING || capture_opts->state == CAPTURE_RUNNING);
270
271   /* free the old filename */
272   if(capture_opts->save_file != NULL) {
273     /* we start a new capture file, close the old one (if we had one before) */
274     /* (we can only have an open capture file in real_time_mode!) */
275     if( ((capture_file *) capture_opts->cf)->state != FILE_CLOSED) {
276         cf_callback_invoke(cf_cb_live_capture_update_finished, capture_opts->cf);
277         cf_finish_tail(capture_opts->cf, &err);
278         cf_close(capture_opts->cf);
279     }
280     g_free(capture_opts->save_file);
281     is_tempfile = FALSE;
282     cf_set_tempfile(capture_opts->cf, FALSE);
283   } else {
284     /* we didn't had a save_file before, must be a tempfile */
285     is_tempfile = TRUE;
286     cf_set_tempfile(capture_opts->cf, TRUE);
287   }
288
289   /* save the new filename */
290   capture_opts->save_file = g_strdup(new_file);
291
292   /* if we are in real-time mode, open the new file now */
293   if(capture_opts->real_time_mode) {
294     /* Attempt to open the capture file and set up to read from it. */
295     switch(cf_start_tail(capture_opts->cf, capture_opts->save_file, is_tempfile, &err)) {
296     case CF_OK:
297       break;
298     case CF_ERROR:
299       /* Don't unlink (delete) the save file - leave it around,
300          for debugging purposes. */
301       g_free(capture_opts->save_file);
302       capture_opts->save_file = NULL;
303       return FALSE;
304       break;
305     }
306   }
307
308   if(capture_opts->show_info) {
309     if (!capture_info_new_file(new_file))
310       return FALSE;
311   }
312
313   if(capture_opts->real_time_mode) {
314     cf_callback_invoke(cf_cb_live_capture_update_started, capture_opts);
315   } else {
316     cf_callback_invoke(cf_cb_live_capture_fixed_started, capture_opts);
317   }
318   capture_opts->state = CAPTURE_RUNNING;
319
320   return TRUE;
321 }
322
323
324 /* capture child tells us we have new packets to read */
325 void
326 capture_input_new_packets(capture_options *capture_opts, int to_read)
327 {
328   int  err;
329
330
331   g_assert(capture_opts->save_file);
332
333   if(capture_opts->real_time_mode) {
334     /* Read from the capture file the number of records the child told us it added. */
335     switch (cf_continue_tail(capture_opts->cf, to_read, &err)) {
336
337     case CF_READ_OK:
338     case CF_READ_ERROR:
339       /* Just because we got an error, that doesn't mean we were unable
340          to read any of the file; we handle what we could get from the
341          file.
342
343          XXX - abort on a read error? */
344          cf_callback_invoke(cf_cb_live_capture_update_continue, capture_opts->cf);
345       break;
346
347     case CF_READ_ABORTED:
348       /* Kill the child capture process; the user wants to exit, and we
349          shouldn't just leave it running. */
350       capture_kill_child(capture_opts);
351       break;
352     }
353   } else {
354     /* increase capture file packet counter by the number or incoming packets */
355     cf_set_packet_count(capture_opts->cf,
356         cf_get_packet_count(capture_opts->cf) + to_read);
357
358     cf_callback_invoke(cf_cb_live_capture_fixed_continue, capture_opts->cf);
359   }
360
361   /* update the main window, so we get events (e.g. from the stop toolbar button) */
362   main_window_update();
363
364   if(capture_opts->show_info)
365     capture_info_new_packets(to_read);
366 }
367
368
369 /* Capture child told us how many dropped packets it counted.
370  */
371 void
372 capture_input_drops(capture_options *capture_opts, int dropped)
373 {
374   g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_INFO, "%d packet%s dropped", dropped, plurality(dropped, "", "s"));
375
376   g_assert(capture_opts->state == CAPTURE_RUNNING);
377
378   cf_set_drops_known(capture_opts->cf, TRUE);
379   cf_set_drops(capture_opts->cf, dropped);
380 }
381
382
383 /* Capture child told us that an error has occurred while starting/running
384    the capture.
385    The buffer we're handed has *two* null-terminated strings in it - a
386    primary message and a secondary message, one right after the other.
387    The secondary message might be a null string.
388  */
389 void
390 capture_input_error_message(capture_options *capture_opts, char *error_msg, char *secondary_error_msg)
391 {
392   gchar *safe_error_msg;
393   gchar *safe_secondary_error_msg;
394
395   g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Error message from child: \"%s\", \"%s\"",
396         error_msg, secondary_error_msg);
397
398   g_assert(capture_opts->state == CAPTURE_PREPARING || capture_opts->state == CAPTURE_RUNNING);
399
400   safe_error_msg = simple_dialog_format_message(error_msg);
401   if (*secondary_error_msg != '\0') {
402     /* We have both primary and secondary messages. */
403     safe_secondary_error_msg = simple_dialog_format_message(secondary_error_msg);
404     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s%s%s\n\n%s",
405                   simple_dialog_primary_start(), safe_error_msg,
406                   simple_dialog_primary_end(), safe_secondary_error_msg);
407     g_free(safe_secondary_error_msg);
408   } else {
409     /* We have only a primary message. */
410     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s%s%s",
411                   simple_dialog_primary_start(), safe_error_msg,
412                   simple_dialog_primary_end());
413   }
414   g_free(safe_error_msg);
415
416   /* the capture child will close the sync_pipe if required, nothing to do for now */
417 }
418
419
420
421 /* Capture child told us that an error has occurred while parsing a
422    capture filter when starting/running the capture.
423  */
424 void
425 capture_input_cfilter_error_message(capture_options *capture_opts, char *error_message)
426 {
427   dfilter_t   *rfcode = NULL;
428   gchar *safe_cfilter = simple_dialog_format_message(capture_opts->cfilter);
429   gchar *safe_cfilter_error_msg = simple_dialog_format_message(error_message);
430
431   g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture filter error message from child: \"%s\"", error_message);
432
433   g_assert(capture_opts->state == CAPTURE_PREPARING || capture_opts->state == CAPTURE_RUNNING);
434
435   /* Did the user try a display filter? */
436   if (dfilter_compile(capture_opts->cfilter, &rfcode) && rfcode != NULL) {
437     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
438       "%sInvalid capture filter: \"%s\"!%s\n"
439       "\n"
440       "That string looks like a valid display filter; however, it isn't a valid\n"
441       "capture filter (%s).\n"
442       "\n"
443       "Note that display filters and capture filters don't have the same syntax,\n"
444       "so you can't use most display filter expressions as capture filters.\n"
445       "\n"
446       "See the User's Guide for a description of the capture filter syntax.",
447       simple_dialog_primary_start(), safe_cfilter,
448       simple_dialog_primary_end(), safe_cfilter_error_msg);
449       dfilter_free(rfcode);
450   } else {
451     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
452       "%sInvalid capture filter: \"%s\"!%s\n"
453       "\n"
454       "That string isn't a valid capture filter (%s).\n"
455       "See the User's Guide for a description of the capture filter syntax.",
456       simple_dialog_primary_start(), safe_cfilter,
457       simple_dialog_primary_end(), safe_cfilter_error_msg);
458   }
459   g_free(safe_cfilter_error_msg);
460   g_free(safe_cfilter);
461
462   /* the capture child will close the sync_pipe if required, nothing to do for now */
463 }
464
465
466 /* capture child closed its side of the pipe, do the required cleanup */
467 void
468 capture_input_closed(capture_options *capture_opts)
469 {
470     int  err;
471     int  packet_count_save;
472
473     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture stopped!");
474     g_assert(capture_opts->state == CAPTURE_PREPARING || capture_opts->state == CAPTURE_RUNNING);
475
476     /* if we didn't started the capture, do a fake start */
477     /* (happens if we got an error message - we won't get a filename then) */
478     if(capture_opts->state == CAPTURE_PREPARING) {
479         if(capture_opts->real_time_mode) {
480             cf_callback_invoke(cf_cb_live_capture_update_started, capture_opts);
481         } else {
482             cf_callback_invoke(cf_cb_live_capture_fixed_started, capture_opts);
483         }
484     }
485
486     if(capture_opts->real_time_mode) {
487         cf_read_status_t status;
488
489         /* Read what remains of the capture file. */
490         status = cf_finish_tail(capture_opts->cf, &err);
491
492         /* XXX: If -Q (quit-after-cap) then cf->count clr'd below so save it first */
493         packet_count_save = cf_get_packet_count(capture_opts->cf);
494         /* Tell the GUI, we are not doing a capture any more.
495                    Must be done after the cf_finish_tail(), so file lengths are displayed
496                    correct. */
497         cf_callback_invoke(cf_cb_live_capture_update_finished, capture_opts->cf);
498
499         /* Finish the capture. */
500         switch (status) {
501
502         case CF_READ_OK:
503             if ((packet_count_save == 0) && !capture_opts->restart) {
504                 simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
505 "%sNo packets captured!%s\n"
506 "\n"
507 "As no data was captured, closing the %scapture file!\n"
508 "\n"
509 "\n"
510 "Help about capturing can be found at:\n"
511 "\n"
512 "       http://wiki.wireshark.org/CaptureSetup"
513 #ifdef _WIN32
514 "\n\n"
515 "Wireless (Wi-Fi/WLAN):\n"
516 "Try to switch off promiscuous mode in the Capture Options!"
517 #endif
518 "",
519               simple_dialog_primary_start(), simple_dialog_primary_end(),
520               cf_is_tempfile(capture_opts->cf) ? "temporary " : "");
521               cf_close(capture_opts->cf);
522             }
523             break;
524         case CF_READ_ERROR:
525           /* Just because we got an error, that doesn't mean we were unable
526              to read any of the file; we handle what we could get from the
527              file. */
528           break;
529
530         case CF_READ_ABORTED:
531           /* Exit by leaving the main loop, so that any quit functions
532              we registered get called. */
533           main_window_quit();
534         }
535
536     } else {
537         /* first of all, we are not doing a capture any more */
538         cf_callback_invoke(cf_cb_live_capture_fixed_finished, capture_opts->cf);
539
540         /* this is a normal mode capture and if no error happened, read in the capture file data */
541         if(capture_opts->save_file != NULL) {
542             capture_input_read_all(capture_opts, cf_is_tempfile(capture_opts->cf),
543                 cf_get_drops_known(capture_opts->cf), cf_get_drops(capture_opts->cf));
544         }
545     }
546
547     if(capture_opts->show_info)
548       capture_info_close();
549
550     capture_opts->state = CAPTURE_STOPPED;
551
552     /* if we couldn't open a capture file, there's nothing more for us to do */
553     if(capture_opts->save_file == NULL) {
554         cf_close(capture_opts->cf);
555         return;
556     }
557
558     /* does the user wants to restart the current capture? */
559     if(capture_opts->restart) {
560         capture_opts->restart = FALSE;
561
562         eth_unlink(capture_opts->save_file);
563
564         /* if it was a tempfile, throw away the old filename (so it will become a tempfile again) */
565         if(cf_is_tempfile(capture_opts->cf)) {
566             g_free(capture_opts->save_file);
567             capture_opts->save_file = NULL;
568         }
569
570         /* ... and start the capture again */
571         capture_start(capture_opts);
572     } else {
573         /* We're not doing a capture any more, so we don't have a save file. */
574         g_free(capture_opts->save_file);
575         capture_opts->save_file = NULL;
576     }
577 }
578
579 /**
580  * Fetch the interface list from a child process (dumpcap).
581  *
582  * @return A GList containing if_info_t structs if successful, NULL otherwise.
583  */
584
585 /* XXX - We parse simple text output to get our interface list.  Should
586  * we use "real" data serialization instead, e.g. via XML? */
587 GList *
588 capture_interface_list(int *err, char **err_str)
589 {
590     GList     *if_list = NULL;
591     int        i, j;
592     gchar     *msg;
593     gchar    **raw_list, **if_parts, **addr_parts;
594     gchar     *name;
595     if_info_t *if_info;
596     if_addr_t *if_addr;
597     struct addrinfo *ai;
598     struct sockaddr_in *sa4;
599     struct sockaddr_in6 *sa6;
600
601     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List ...");
602
603     /* Try to get our interface list */
604     *err = sync_interface_list_open(&msg);
605     if (*err != 0) {
606         g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List failed!");
607         if (err_str) {
608             if (*err_str)
609                 *err_str = msg;
610             else
611                 g_free(msg);
612         } else {
613             g_free(msg);
614         }
615         return NULL;
616     }
617
618     /* Split our lines */
619     raw_list = g_strsplit(msg, "\n", 0);
620     g_free(msg);
621
622     for (i = 0; raw_list[i] != NULL; i++) {
623         if_parts = g_strsplit(raw_list[i], "\t", 4);
624         if (if_parts[0] == NULL || if_parts[1] == NULL || if_parts[2] == NULL ||
625                 if_parts[3] == NULL) {
626             g_strfreev(if_parts);
627             continue;
628         }
629
630         /* Number followed by the name, e.g "1. eth0" */
631         name = strchr(if_parts[0], ' ');
632         if (name) {
633             name++;
634         } else {
635             g_strfreev(if_parts);
636             continue;
637         }
638
639         if_info = g_malloc0(sizeof(if_info_t));
640         if_info->name = g_strdup(name);
641         if (strlen(if_parts[1]) > 0)
642             if_info->description = g_strdup(if_parts[1]);
643         addr_parts = g_strsplit(if_parts[2], ",", 0);
644         for (j = 0; addr_parts[j] != NULL; j++) {
645             /* XXX - We're failing to convert IPv6 addresses (on Ubuntu, at least) */
646             if (getaddrinfo(addr_parts[j], NULL, NULL, &ai) == 0) {
647                 if_addr = NULL;
648                 switch (ai->ai_family) {
649                     case AF_INET:
650                         if_addr = g_malloc0(sizeof(if_addr_t));
651                         if_addr->type = AT_IPv4;
652                         sa4 = (struct sockaddr_in *) ai->ai_addr;
653                         if_addr->ip_addr.ip4_addr = sa4->sin_addr.s_addr;
654                         break;
655                     case AF_INET6:
656                         if_addr = g_malloc0(sizeof(if_addr_t));
657                         if_addr->type = AT_IPv6;
658                         sa6 = (struct sockaddr_in6 *) ai->ai_addr;
659                         memcpy(&if_addr->ip_addr.ip6_addr, sa6->sin6_addr.s6_addr, 16);
660                         break;
661                 }
662                 if (if_addr) {
663                     if_info->ip_addr = g_slist_append(if_info->ip_addr, if_addr);
664                 }
665                 freeaddrinfo(ai);
666             }
667         }
668         if (strcmp(if_parts[3], "loopback") == 0)
669             if_info->loopback = TRUE;
670         g_strfreev(if_parts);
671         g_strfreev(addr_parts);
672         if_list = g_list_append(if_list, if_info);
673     }
674     g_strfreev(raw_list);
675
676     /* Check to see if we built a list */
677     if (if_list == NULL) {
678         if (*err_str)
679             *err_str = g_strdup("No interfaces found");
680         *err = NO_INTERFACES_FOUND;
681     }
682     return if_list;
683 }
684
685
686 #endif /* HAVE_LIBPCAP */