1 /* capture_ui_utils.c
2  * Utilities for capture user interfaces
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  */
10 
11 #include "config.h"
12 
13 #ifdef HAVE_LIBPCAP
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <glib.h>
19 
20 #include "epan/prefs.h"
21 #include "epan/ex-opt.h"
22 #include "capture/capture_ifinfo.h"
23 #include "ui/capture_ui_utils.h"
24 #include "ui/capture_globals.h"
25 #include "wiretap/wtap.h"
26 #include "epan/to_str.h"
27 #include "wsutil/strtoi.h"
28 
29 /*
30  * In a list of interface information, in the form of a comma-separated
31  * list of {name}({property}) items, find the entry for a particular
32  * interface, and return a pointer a g_malloced string containing
33  * the property.
34  */
35 static char *
capture_dev_get_if_property(const gchar * pref,const gchar * if_name)36 capture_dev_get_if_property(const gchar *pref, const gchar *if_name)
37 {
38     gchar **if_tokens;
39     gchar *property = NULL;
40     int i;
41 
42     if (if_name == NULL || strlen(if_name) < 1) {
43         return NULL;
44     }
45 
46     if (pref == NULL || strlen(pref) < 1) {
47         /* There is no interface information list. */
48         return NULL;
49     }
50 
51     /*
52      * Split the list into a sequence of items.
53      *
54      * XXX - this relies on the items not themselves containing commas.
55      */
56     if_tokens = g_strsplit(pref, ",", -1);
57     for (i = 0; if_tokens[i] != NULL; i++) {
58         gchar *opening_parenp, *closing_parenp;
59 
60         /*
61          * Separate this item into name and property.
62          * The first opening parenthesis and the last closing parenthesis
63          * surround the property.  Any other parentheses are part of
64          * the property.
65          */
66         opening_parenp = strchr(if_tokens[i], '(');
67         if (opening_parenp == NULL) {
68             /* No opening parenthesis. Give up. */
69             break;
70         }
71         closing_parenp = strrchr(if_tokens[i], ')');
72         if (closing_parenp == NULL || closing_parenp <= opening_parenp) {
73             /* No closing parenthesis or invalid input. Give up. */
74             break;
75         }
76         *opening_parenp = '\0'; /* Split {name} from what follows */
77         *closing_parenp = '\0'; /* Terminate {property} */
78         if (strcmp(if_tokens[i], if_name) == 0) {
79             if (strlen(opening_parenp + 1) > 0) {
80                 property = g_strdup(opening_parenp + 1);
81             }
82             break;
83         }
84     }
85     g_strfreev(if_tokens);
86 
87     return property;
88 }
89 
90 /*
91  * Find a property that should be an integral value, and return the
92  * value or, if it's not found or not a valid integral value, -1.
93  */
94 static gint
capture_dev_get_if_int_property(const gchar * pref,const gchar * if_name)95 capture_dev_get_if_int_property(const gchar *pref, const gchar *if_name)
96 {
97     gchar *property_string;
98     gint property;
99 
100     property_string = capture_dev_get_if_property(pref, if_name);
101     if (property_string == NULL) {
102         /* No property found for this interface. */
103         return -1;
104     }
105     if (!ws_strtoi(property_string, NULL, &property)) {
106         /* Syntax error or range error */
107         g_free(property_string);
108         return -1;
109     }
110 
111     g_free(property_string);
112     return property;
113 }
114 
115 /*
116  * Find user-specified capture device description that matches interface
117  * name, if any.
118  */
119 char *
capture_dev_user_descr_find(const gchar * if_name)120 capture_dev_user_descr_find(const gchar *if_name)
121 {
122     return capture_dev_get_if_property(prefs.capture_devices_descr, if_name);
123 }
124 
125 gint
capture_dev_user_linktype_find(const gchar * if_name)126 capture_dev_user_linktype_find(const gchar *if_name)
127 {
128     return capture_dev_get_if_int_property(prefs.capture_devices_linktypes, if_name);
129 }
130 
131 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
132 gint
capture_dev_user_buffersize_find(const gchar * if_name)133 capture_dev_user_buffersize_find(const gchar *if_name)
134 {
135     return capture_dev_get_if_int_property(prefs.capture_devices_buffersize, if_name);
136 }
137 #endif
138 
139 gboolean
capture_dev_user_snaplen_find(const gchar * if_name,gboolean * hassnap,int * snaplen)140 capture_dev_user_snaplen_find(const gchar *if_name, gboolean *hassnap, int *snaplen)
141 {
142     gboolean found = FALSE;
143     gchar **if_tokens;
144     int i;
145 
146     if (if_name == NULL || strlen(if_name) < 1) {
147         return FALSE;
148     }
149 
150     if ((prefs.capture_devices_snaplen == NULL) ||
151             (*prefs.capture_devices_snaplen == '\0')) {
152         /* There are no snap lengths defined */
153         return FALSE;
154     }
155 
156     /*
157      * Split the list into a sequence of items.
158      *
159      * XXX - this relies on the items not themselves containing commas.
160      */
161     if_tokens = g_strsplit(prefs.capture_devices_snaplen, ",", -1);
162     for (i = 0; if_tokens[i] != NULL; i++) {
163         gchar *colonp;
164         const gchar *next;
165         gint value;
166 
167         /*
168          * This one's a bit ugly.
169          * The syntax of the item is {name}:{hassnap}({snaplen}),
170          * where {hassnap} is 0 if the interface shouldn't have a snapshot
171          * length and 1 if it should, and {snaplen} is the maximum snapshot
172          * length if {hassnap} is 0 and the specified snapshot length if
173          * {hassnap} is 1.
174          *
175          * Sadly, : was a bad choice of separator, given that, on some OSes,
176          * an interface can have a colon in its name.
177          *
178          * So we look for the *last* colon in the string.
179          */
180         colonp = strrchr(if_tokens[i], ':');
181         if (colonp == NULL) {
182             /* No separating colon. Give up. */
183             break;
184         }
185         *colonp = '\0'; /* Split {name} from what follows */
186         if (strcmp(if_tokens[i], if_name) == 0) {
187             /* OK, this matches. */
188             if (*(colonp + 1) == '0') {
189                 /* {hassnap} is false, so just set the snaplen to WTAP_MAX_PACKET_SIZE_STANDARD. */
190                 found = TRUE;
191                 *hassnap = FALSE;
192                 *snaplen = WTAP_MAX_PACKET_SIZE_STANDARD;
193             } else if (*(colonp + 1) == '1') {
194                 /* {hassnap} is true, so extract {snaplen} */
195                 if (*(colonp + 2) != '(') {
196                     /* Not followed by a parenthesis. Give up. */
197                     break;
198                 }
199                 if (!ws_strtoi(colonp + 3, &next, &value) ||
200                     next == colonp + 3 || *next != ')' || value < 0) {
201                     /* Syntax error or range error. Give up. */
202                     break;
203                 }
204                 found = TRUE;
205                 *hassnap = TRUE;
206                 *snaplen = value;
207             } else {
208                 /* Bad {hassnap}. Give up. */
209                 break;
210             }
211             break;
212         }
213     }
214     g_strfreev(if_tokens);
215 
216     return found;
217 }
218 
219 gboolean
capture_dev_user_pmode_find(const gchar * if_name,gboolean * pmode)220 capture_dev_user_pmode_find(const gchar *if_name, gboolean *pmode)
221 {
222     int value;
223 
224     value = capture_dev_get_if_int_property(prefs.capture_devices_pmode, if_name);
225     if (value == -1) {
226         /* Not found or bad. */
227         return FALSE;
228     }
229     *pmode = (value != 0);
230     return TRUE;
231 }
232 
233 gchar*
capture_dev_user_cfilter_find(const gchar * if_name)234 capture_dev_user_cfilter_find(const gchar *if_name)
235 {
236     return capture_dev_get_if_property(prefs.capture_devices_filter, if_name);
237 }
238 
239 /*
240  * Return as descriptive a name for an interface as we can get.
241  * If the user has specified a comment, use that.  Otherwise,
242  * if capture_interface_list() supplies a description, use that,
243  * otherwise use the interface name.
244  *
245  * The result must be g_free()'d when you're done with it.
246  *
247  * Note: given that this calls capture_interface_list(), which attempts to
248  * open all adapters it finds in order to check whether they can be
249  * captured on, this is an expensive routine to call, so don't call it
250  * frequently.
251  */
252 char *
get_interface_descriptive_name(const char * if_name)253 get_interface_descriptive_name(const char *if_name)
254 {
255     char *descr;
256     GList *if_list;
257     GList *if_entry;
258     if_info_t *if_info;
259     int err;
260 
261     /* Do we have a user-supplied description? */
262     descr = capture_dev_user_descr_find(if_name);
263     if (descr == NULL) {
264         /* No; try to construct a descriptive name. */
265         if (strcmp(if_name, "-") == 0) {
266             /*
267              * Strictly speaking, -X (extension) options are for modules, e.g. Lua
268              * and using one here stretches that definition. However, this doesn't
269              * waste a single-letter option on something that might be rarely used
270              * and is backward-compatible to 1.0.
271              */
272             descr = g_strdup(ex_opt_get_nth("stdin_descr", 0));
273             if (!descr) {
274                 descr = g_strdup("Standard input");
275             }
276         } else {
277             /* No, we don't have a user-supplied description; did we get
278                one from the OS or libpcap? */
279             if_list = capture_interface_list(&err, NULL, NULL);
280             if (if_list != NULL) {
281                 if_entry = if_list;
282                 do {
283                     if_info = (if_info_t *)if_entry->data;
284                     if (strcmp(if_info->name, if_name) == 0) {
285                         if (if_info->friendly_name != NULL) {
286                             /* We have a "friendly name"; return a copy of that
287                                as the description - when we free the interface
288                                list, that'll also free up the strings to which
289                                it refers. */
290                             descr = g_strdup(if_info->friendly_name);
291                         } else if (if_info->vendor_description != NULL) {
292                             /* We have no "friendly name", but we have a vendor
293                                description; return a copy of that - when we free
294                                the interface list, that'll also free up the strings
295                                to which it refers. */
296                             descr = g_strdup(if_info->vendor_description);
297                         }
298                         break;
299                     }
300                 } while ((if_entry = g_list_next(if_entry)) != NULL);
301             }
302             free_interface_list(if_list);
303 
304             if (descr == NULL) {
305                 /* The interface name is all we have, so just return a copy of that. */
306                 descr = g_strdup(if_name);
307             }
308         }
309     }
310 
311     return descr;
312 }
313 
314 GList *
build_capture_combo_list(GList * if_list,gboolean do_hide)315 build_capture_combo_list(GList *if_list, gboolean do_hide)
316 {
317     GList *combo_list;
318     GList *if_entry;
319     if_info_t *if_info;
320     char *if_string;
321     gchar *descr;
322 
323     combo_list = NULL;
324     if (if_list != NULL) {
325         /* Scan through the list and build a list of strings to display. */
326         for (if_entry = if_list; if_entry != NULL;
327                 if_entry = g_list_next(if_entry)) {
328             if_info = (if_info_t *)if_entry->data;
329 
330             /* Is this interface hidden and, if so, should we include it
331                anyway? */
332             if (!prefs_is_capture_device_hidden(if_info->name) || !do_hide) {
333                 /* It's not hidden, or it is but we should include it in the list. */
334 
335                 /* Do we have a user-supplied description? */
336                 descr = capture_dev_user_descr_find(if_info->name);
337                 if (descr != NULL) {
338                     /* Yes, we have a user-supplied description; use it. */
339                     if_string = g_strdup_printf("%s: %s", descr, if_info->name);
340                     g_free(descr);
341                 } else {
342                     /* No, we don't have a user-supplied description; did we get
343                        one from the OS or libpcap? */
344                     if (if_info->vendor_description != NULL) {
345                         /* Yes - use it. */
346                         if_string = g_strdup_printf("%s: %s",
347                                 if_info->vendor_description,
348                                 if_info->name);
349                     } else {
350                         /* No. */
351                         if_string = g_strdup(if_info->name);
352                     }
353                 }
354                 combo_list = g_list_prepend(combo_list, if_string);
355             }
356         }/*for*/
357         if(combo_list){
358             combo_list = g_list_reverse(combo_list);
359         }
360     }
361     return combo_list;
362 }
363 
364 static void
free_if_string(gpointer data,gpointer user_data _U_)365 free_if_string(gpointer data, gpointer user_data _U_)
366 {
367     g_free(data);
368 }
369 
370 void
free_capture_combo_list(GList * combo_list)371 free_capture_combo_list(GList *combo_list)
372 {
373     if (combo_list != NULL) {
374         g_list_foreach(combo_list, free_if_string, NULL);
375         g_list_free(combo_list);
376     }
377 }
378 
379 /*
380  * Given text that contains an interface name possibly prefixed by an
381  * interface description, extract the interface name.
382  */
383 const char *
get_if_name(const char * if_text)384 get_if_name(const char *if_text)
385 {
386     const char *if_name;
387 
388 #ifdef _WIN32
389     /*
390      * We cannot assume that the interface name doesn't contain a space;
391      * some names on Windows OT do.
392      *
393      * We also can't assume it begins with "\Device\", either, as, on
394      * Windows OT, WinPcap doesn't put "\Device\" in front of the name.
395      *
396      * XXX - we don't support Windows OT any more; do we need to worry
397      * about this?
398      *
399      * As I remember, we can't assume that the interface description
400      * doesn't contain a colon, either; I think some do.
401      *
402      * We can probably assume that the interface *name* doesn't contain
403      * a colon, however; if any interface name does contain a colon on
404      * Windows, it'll be time to just get rid of the damn interface
405      * descriptions in the drop-down list, have just the names in the
406      * drop-down list, and have a "Browse..." button to browse for interfaces,
407      * with names, descriptions, IP addresses, blah blah blah available when
408      * possible.
409      *
410      * So we search backwards for a colon.  If we don't find it, just
411      * return the entire string; otherwise, skip the colon and any blanks
412      * after it, and return that string.
413      */
414     if_name = if_text + strlen(if_text);
415     for (;;) {
416         if (if_name == if_text) {
417             /* We're at the beginning of the string; return it. */
418             break;
419         }
420         if_name--;
421         if (*if_name == ':') {
422             /*
423              * We've found a colon.
424              * Unfortunately, a colon is used in the string "rpcap://",
425              * which is used in case of a remote capture.
426              * So we'll check to make sure the colon isn't followed by "//";
427              * it'll be followed by a blank if it separates the description
428              * and the interface name.  (We don't wire in "rpcap", in case we
429              * support other protocols in the same syntax.)
430              * Unfortunately, another colon can be used in "rpcap://host:port/"
431              * before port. Check if colon is followed by digit.
432              */
433             if ((strncmp(if_name, "://", 3) != 0) && !g_ascii_isdigit(if_name[1])) {
434                 /*
435                  * OK, we've found a colon followed neither by "//" nor by digit.
436                  * Skip blanks following it.
437                  */
438                 if_name++;
439                 while (*if_name == ' ')
440                     if_name++;
441                 break;
442             }
443         }
444         /* Keep looking for a colon not followed by "//". */
445     }
446 #else
447     /*
448      * There's a space between the interface description and name, and
449      * the interface name shouldn't have a space in it (it doesn't, on
450      * UNIX systems); look backwards in the string for a space.
451      *
452      * (An interface name might, however, contain a colon in it, which
453      * is why we don't use the colon search on UNIX.)
454      */
455     if_name = strrchr(if_text, ' ');
456     if (if_name == NULL) {
457         if_name = if_text;
458     } else {
459         if_name++;
460     }
461 #endif
462     return if_name;
463 }
464 
465 /*
466  * Set the active DLT for a device appropriately.
467  */
468 void
set_active_dlt(interface_t * device,int global_default_dlt)469 set_active_dlt(interface_t *device, int global_default_dlt)
470 {
471     GList    *list;
472     gboolean  found_active_dlt;
473     link_row *link;
474 
475     /*
476      * If there's a preference for the link-layer header type for
477      * this interface, use it.  If not, use the all-interface
478      * default; if that's not set on the command line, that will
479      * be -1, meaning "use per-interface defaults", otherwise
480      * we'll fail if it's not one of the types the interface
481      * supports.
482      */
483     if ((device->active_dlt = capture_dev_user_linktype_find(device->name)) == -1) {
484         device->active_dlt = global_default_dlt;
485     }
486 
487     /*
488      * Is that one of the supported link-layer header types?
489      * If not, set it to -1, so we'll fall back on the first supported
490      * link-layer header type.
491      */
492     found_active_dlt = FALSE;
493     for (list = device->links; list != NULL; list = g_list_next(list)) {
494         link = (link_row *)(list->data);
495         if (link->dlt != -1 && link->dlt == device->active_dlt) {
496             found_active_dlt = TRUE;
497             break;
498         }
499     }
500     if (!found_active_dlt) {
501         device->active_dlt = -1;
502     }
503     if (device->active_dlt == -1) {
504         /* Fall back on the first supported DLT, if we have one. */
505         for (list = device->links; list != NULL; list = g_list_next(list)) {
506             link = (link_row *)(list->data);
507             if (link->dlt != -1) {
508                 device->active_dlt = link->dlt;
509                 break;
510             }
511         }
512     }
513 }
514 
515 GString *
get_iface_list_string(capture_options * capture_opts,guint32 style)516 get_iface_list_string(capture_options *capture_opts, guint32 style)
517 {
518     GString *iface_list_string = g_string_new("");
519     guint i;
520 
521     /*
522      * If we have a descriptive name for the interface, show that,
523      * rather than its raw name.  On NT 5.x (2K/XP/Server2K3), the
524      * interface name is something like "\Device\NPF_{242423..."
525      * which is pretty useless to the normal user.  On other platforms,
526      * it might be less cryptic, but if a more descriptive name is
527      * available, we should still use that.
528      */
529 #ifdef _WIN32
530     if (capture_opts->ifaces->len < 2) {
531 #else
532     if (capture_opts->ifaces->len < 4) {
533 #endif
534         for (i = 0; i < capture_opts->ifaces->len; i++) {
535             if (i > 0) {
536                 if (capture_opts->ifaces->len > 2) {
537                     g_string_append_printf(iface_list_string, ",");
538                 }
539                 g_string_append_printf(iface_list_string, " ");
540                 if (i == capture_opts->ifaces->len - 1) {
541                     g_string_append_printf(iface_list_string, "and ");
542                 }
543             }
544 
545             interface_options *interface_opts = &g_array_index(capture_opts->ifaces, interface_options, i);
546 
547             if (style & IFLIST_QUOTE_IF_DESCRIPTION)
548                 g_string_append_printf(iface_list_string, "'");
549             if (interface_opts->display_name == NULL) {
550                 /*
551                  * We don't have a display name; generate one.
552                  */
553                 if (interface_opts->descr == NULL) {
554                     if (interface_opts->name != NULL)
555                         interface_opts->descr = get_interface_descriptive_name(interface_opts->name);
556                     else
557                         interface_opts->descr = g_strdup("(Unknown)");
558                 }
559                 interface_opts->display_name = g_strdup(interface_opts->descr);
560             }
561             g_string_append_printf(iface_list_string, "%s", interface_opts->display_name);
562             if (style & IFLIST_QUOTE_IF_DESCRIPTION)
563                 g_string_append_printf(iface_list_string, "'");
564             if (style & IFLIST_SHOW_FILTER) {
565                 if (interface_opts->cfilter != NULL &&
566                         strlen(interface_opts->cfilter) > 0) {
567                     g_string_append_printf(iface_list_string, " (%s)", interface_opts->cfilter);
568                 }
569             }
570         }
571     } else {
572         g_string_append_printf(iface_list_string, "%u interfaces", capture_opts->ifaces->len);
573     }
574     return iface_list_string;
575 }
576 #endif /* HAVE_LIBPCAP */
577