b61f6dbbb4375475d0a2e519c6081f536d242225
[metze/wireshark/wip.git] / ui / qt / capture_filter_syntax_worker.cpp
1 /* capture_filter_syntax_worker.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "config.h"
23
24 #ifdef HAVE_LIBPCAP
25 #include <glib.h>
26
27 #include <wsutil/wspcap.h>
28
29 #include "capture_opts.h"
30 #include "ui/capture_globals.h"
31 #endif
32 #ifdef HAVE_EXTCAP
33 #include "extcap.h"
34 #endif
35
36 #include "capture_filter_syntax_worker.h"
37 #include <ui/qt/widgets/syntax_line_edit.h>
38
39 #include <QMutexLocker>
40 #include <QSet>
41
42 // We use a global mutex to protect pcap_compile since it calls gethostbyname.
43 // This probably isn't needed on Windows (where pcap_comple calls
44 // EnterCriticalSection + LeaveCriticalSection) or *BSD or macOS where
45 // gethostbyname(3) claims that it's thread safe.
46 static QMutex pcap_compile_mtx_;
47
48 #if 0
49 #include <QDebug>
50 #include <QThread>
51 #define DEBUG_SYNTAX_CHECK(state1, state2) qDebug() << "CF state" << QThread::currentThreadId() << state1 << "->" << state2 << ":" << filter_text_ << ":" << filter
52 #define DEBUG_SLEEP_TIME 5000 // ms
53 #else
54 #define DEBUG_SYNTAX_CHECK(state1, state2)
55 #define DEBUG_SLEEP_TIME 0 // ms
56 #endif
57
58 #define DUMMY_SNAPLENGTH                65535
59 #define DUMMY_NETMASK                   0xFF000000
60
61 void CaptureFilterSyntaxWorker::start() {
62 #ifdef HAVE_LIBPCAP
63     forever {
64         QString filter;
65         QSet<gint> active_dlts;
66 #ifdef HAVE_EXTCAP
67         QSet<guint> active_extcap;
68 #endif
69         struct bpf_program fcode;
70         pcap_t *pd;
71         int pc_err;
72         enum SyntaxLineEdit::SyntaxState state = SyntaxLineEdit::Valid;
73         QString err_str;
74
75         data_mtx_.lock();
76         while (filter_text_.isEmpty()) {
77             data_cond_.wait(&data_mtx_);
78         }
79
80         DEBUG_SYNTAX_CHECK("pending", "unknown");
81         filter = filter_text_;
82         filter_text_ = QString();
83         data_mtx_.unlock();
84
85         if (global_capture_opts.num_selected < 1) {
86             emit syntaxResult(filter, SyntaxLineEdit::Invalid, QString("No interfaces selected"));
87             DEBUG_SYNTAX_CHECK("unknown", "no interfaces");
88             continue;
89         }
90
91         for (guint if_idx = 0; if_idx < global_capture_opts.all_ifaces->len; if_idx++) {
92             interface_t *device;
93
94             device = &g_array_index(global_capture_opts.all_ifaces, interface_t, if_idx);
95             if (device->selected) {
96 #ifdef HAVE_EXTCAP
97                 if (device->if_info.extcap == NULL || strlen(device->if_info.extcap) == 0) {
98 #endif
99                     if (device->active_dlt >= DLT_USER0 && device->active_dlt <= DLT_USER15) {
100                         // Capture filter for DLT_USER is unknown
101                         state = SyntaxLineEdit::Deprecated;
102                         err_str = "Unable to check capture filter";
103                     } else {
104                         active_dlts.insert(device->active_dlt);
105                     }
106 #ifdef HAVE_EXTCAP
107                 } else {
108                     active_extcap.insert(if_idx);
109                 }
110 #endif
111             }
112         }
113
114         foreach (gint dlt, active_dlts.toList()) {
115             pcap_compile_mtx_.lock();
116             pd = pcap_open_dead(dlt, DUMMY_SNAPLENGTH);
117             if (pd == NULL)
118             {
119                 //don't have ability to verify capture filter
120                 break;
121             }
122 #ifdef PCAP_NETMASK_UNKNOWN
123             pc_err = pcap_compile(pd, &fcode, filter.toUtf8().constData(), 1 /* Do optimize */, PCAP_NETMASK_UNKNOWN);
124 #else
125             pc_err = pcap_compile(pd, &fcode, filter.toUtf8().constData(), 1 /* Do optimize */, 0);
126 #endif
127
128 #if DEBUG_SLEEP_TIME > 0
129             QThread::msleep(DEBUG_SLEEP_TIME);
130 #endif
131
132             if (pc_err) {
133                 DEBUG_SYNTAX_CHECK("unknown", "known bad");
134                 state = SyntaxLineEdit::Invalid;
135                 err_str = pcap_geterr(pd);
136             } else {
137                 DEBUG_SYNTAX_CHECK("unknown", "known good");
138             }
139             pcap_close(pd);
140
141             pcap_compile_mtx_.unlock();
142
143             if (state == SyntaxLineEdit::Invalid) break;
144         }
145 #ifdef HAVE_EXTCAP
146         // If it's already invalid, don't bother to check extcap
147         if (state != SyntaxLineEdit::Invalid) {
148             foreach (guint extcapif, active_extcap.toList()) {
149                 interface_t *device;
150                 gchar *error = NULL;
151
152                 device = &g_array_index(global_capture_opts.all_ifaces, interface_t, extcapif);
153                 extcap_filter_status status = extcap_verify_capture_filter(device->name, filter.toUtf8().constData(), &error);
154                 if (status == EXTCAP_FILTER_VALID) {
155                     DEBUG_SYNTAX_CHECK("unknown", "known good");
156                 } else if (status == EXTCAP_FILTER_INVALID) {
157                     DEBUG_SYNTAX_CHECK("unknown", "known bad");
158                     state = SyntaxLineEdit::Invalid;
159                     err_str = error;
160                     break;
161                 } else {
162                     state = SyntaxLineEdit::Deprecated;
163                     err_str = "Unable to check capture filter";
164                 }
165                 g_free (error);
166             }
167         }
168 #endif
169         emit syntaxResult(filter, state, err_str);
170
171         DEBUG_SYNTAX_CHECK("known", "idle");
172     }
173 #endif // HAVE_LIBPCAP
174 }
175
176 void CaptureFilterSyntaxWorker::checkFilter(const QString &filter)
177 {
178 #ifdef HAVE_LIBPCAP
179     QMutexLocker ml(&data_mtx_);
180     /* Ruthlessly clobber the current state. */
181     filter_text_ = filter;
182     DEBUG_SYNTAX_CHECK("received", "?");
183     data_cond_.wakeOne();
184 #else
185     emit syntaxResult(filter, SyntaxLineEdit::Deprecated, QString("Syntax checking unavailable"));
186 #endif // HAVE_LIBPCAP
187 }
188
189 /*
190  * Editor modelines
191  *
192  * Local Variables:
193  * c-basic-offset: 4
194  * tab-width: 8
195  * indent-tabs-mode: nil
196  * End:
197  *
198  * ex: set shiftwidth=4 tabstop=8 expandtab:
199  * :indentSize=4:tabSize=8:noTabs=true:
200  */