1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 // Copyright (C) 2013-2013 Sourcefire, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License Version 2 as published
7 // by the Free Software Foundation.  You may not use, modify or distribute
8 // this program under any other version of the GNU General Public License.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 //--------------------------------------------------------------------------
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "trough.h"
25 
26 #include <fnmatch.h>
27 #include <sys/stat.h>
28 
29 #include <algorithm>
30 #include <fstream>
31 
32 #include "helpers/directory.h"
33 #include "log/messages.h"
34 #include "main/snort_config.h"
35 #include "utils/util.h"
36 
37 using namespace snort;
38 
39 std::vector<struct Trough::PcapReadObject> Trough::pcap_object_list;
40 std::vector<std::string> Trough::pcap_queue;
41 std::string Trough::pcap_filter = "*.*cap*";
42 std::vector<std::string>::const_iterator Trough::pcap_queue_iter;
43 
44 unsigned Trough::pcap_loop_count = 0;
45 std::atomic<unsigned> Trough::file_count{0};
46 
add_pcaps_dir(const std::string & dirname,const std::string & filter)47 bool Trough::add_pcaps_dir(const std::string& dirname, const std::string& filter)
48 {
49     Directory pcap_dir(dirname.c_str(), filter.c_str());
50     if (pcap_dir.error_on_open())
51     {
52         ErrorMessage("Error getting pcaps under dir: %s: %s\n",
53                 dirname.c_str(), get_error(pcap_dir.error_on_open()));
54         return false;
55     }
56 
57     std::vector<std::string> tmp_queue;
58     const char* pcap_filename;
59     while ((pcap_filename = pcap_dir.next()))
60         tmp_queue.emplace_back(pcap_filename);
61     std::sort(tmp_queue.begin(), tmp_queue.end());
62 
63     pcap_queue.reserve(pcap_queue.size() + tmp_queue.size());
64     pcap_queue.insert(pcap_queue.end(), tmp_queue.begin(), tmp_queue.end());
65 
66     return true;
67 }
68 
add_pcaps_list_file(const std::string & list_filename,const std::string & filter)69 bool Trough::add_pcaps_list_file(const std::string& list_filename, const std::string& filter)
70 {
71     std::ifstream pcap_list_file(list_filename);
72     if (!pcap_list_file.is_open())
73     {
74         ErrorMessage("Could not open pcap list file: %s: %s\n", list_filename.c_str(), get_error(errno));
75         return false;
76     }
77 
78     std::string pcap_name;
79     while (getline(pcap_list_file, pcap_name))
80     {
81         /* Trim leading and trailing whitespace. */
82         constexpr const char* whitespace = " \f\n\r\t\v";
83         pcap_name.erase(0, pcap_name.find_first_not_of(whitespace));
84         pcap_name.erase(pcap_name.find_last_not_of(whitespace) + 1);
85 
86         if (pcap_name.empty())
87             continue;
88 
89         /* do a quick check to make sure file exists */
90         struct stat sb;
91         if (stat(pcap_name.c_str(), &sb) == -1)
92         {
93             ErrorMessage("Error getting stat on pcap file: %s: %s\n", pcap_name.c_str(), get_error(errno));
94             pcap_list_file.close();
95             return false;
96         }
97         if (S_ISDIR(sb.st_mode))
98         {
99             if (!add_pcaps_dir(pcap_name, filter))
100             {
101                 pcap_list_file.close();
102                 return false;
103             }
104         }
105         else if (S_ISREG(sb.st_mode))
106         {
107             if (filter.empty() || (fnmatch(filter.c_str(), pcap_name.c_str(), 0) == 0))
108                 pcap_queue.emplace_back(pcap_name);
109         }
110         else
111         {
112             ErrorMessage("Specified entry in \'%s\' is not a regular file or directory: %s\n",
113                     list_filename.c_str(), pcap_name.c_str());
114             pcap_list_file.close();
115             return false;
116         }
117     }
118     pcap_list_file.close();
119 
120     return true;
121 }
122 
add_pcaps_list(const std::string & list)123 bool Trough::add_pcaps_list(const std::string& list)
124 {
125     if (list.empty())
126     {
127         ErrorMessage("No pcaps specified in pcap list\n");
128         return false;
129     }
130 
131     std::string pcap_name;
132     size_t i = 0;
133     size_t pos = 0;
134 
135     do
136     {
137         pos = list.find(' ', i);
138         if (pos == std::string::npos)
139             pcap_name = list.substr(i);
140         else
141         {
142             pcap_name = list.substr(i, pos - i);
143             i = ++pos;
144         }
145         /* do a quick check to make sure file exists */
146         if (pcap_name != "-")
147         {
148             struct stat sb;
149             if (stat(pcap_name.c_str(), &sb) == -1)
150             {
151                 ErrorMessage("Error getting stat on file: %s: %s (%d)\n",
152                         pcap_name.c_str(), get_error(errno), errno);
153                 return false;
154             }
155             if (!(sb.st_mode & (S_IFREG|S_IFIFO)))
156             {
157                 ErrorMessage("Specified pcap is not a regular file: %s\n", pcap_name.c_str());
158                 return false;
159             }
160         }
161 
162         pcap_queue.emplace_back(pcap_name);
163     } while (pos != std::string::npos);
164 
165     return true;
166 }
167 
get_pcaps(const std::vector<struct PcapReadObject> & pol)168 bool Trough::get_pcaps(const std::vector<struct PcapReadObject> &pol)
169 {
170     for (const PcapReadObject &pro : pol)
171     {
172         switch (pro.type)
173         {
174             case SOURCE_FILE_LIST:
175                 /* arg should be a file with a list of pcaps in it */
176                 if (!add_pcaps_list_file(pro.arg, pro.filter))
177                     return false;
178                 break;
179 
180             case SOURCE_LIST:
181                 /* arg should be a space separated list of pcaps */
182                 if (!add_pcaps_list(pro.arg))
183                     return false;
184                 break;
185 
186             case SOURCE_DIR:
187                 /* arg should be a directory name */
188                 if (!add_pcaps_dir(pro.arg, pro.filter))
189                     return false;
190                 break;
191         }
192     }
193 
194     return true;
195 }
196 
add_source(SourceType type,const char * list)197 void Trough::add_source(SourceType type, const char* list)
198 {
199     PcapReadObject pro;
200 
201     pro.type = type;
202     pro.arg = list;
203     pro.filter = pcap_filter;
204 
205     pcap_object_list.emplace_back(pro);
206 }
207 
set_filter(const char * f)208 void Trough::set_filter(const char* f)
209 {
210     if (f)
211         pcap_filter = f;
212     else
213         pcap_filter.erase();
214 }
215 
setup()216 void Trough::setup()
217 {
218     if (!pcap_object_list.empty())
219     {
220         if (!get_pcaps(pcap_object_list))
221             FatalError("Error getting pcaps.\n");
222 
223         if (pcap_queue.empty())
224             FatalError("No pcaps found.\n");
225 
226         /* free pcap list used to get params */
227         pcap_object_list.clear();
228 
229         pcap_queue_iter = pcap_queue.cbegin();
230     }
231     pcap_filter.clear();
232 }
233 
cleanup()234 void Trough::cleanup()
235 {
236     /* clean up pcap queues */
237     pcap_queue.clear();
238 }
239 
get_next()240 const char* Trough::get_next()
241 {
242     const char* pcap = nullptr;
243 
244     if (pcap_queue.empty() || pcap_queue_iter == pcap_queue.cend())
245         return nullptr;
246 
247     pcap = pcap_queue_iter->c_str();
248     ++pcap_queue_iter;
249     /* If we've reached the end, reset the iterator if we have more
250         loops to cover. */
251     if (pcap_queue_iter == pcap_queue.cend() && pcap_loop_count > 1)
252     {
253         pcap_loop_count--;
254         pcap_queue_iter = pcap_queue.cbegin();
255     }
256 
257     file_count++;
258     return pcap;
259 }
260 
has_next()261 bool Trough::has_next()
262 {
263     return (!pcap_queue.empty() && pcap_queue_iter != pcap_queue.cend());
264 }
265 
266