Commit | Line | Data |
---|---|---|
1d110d20 CH |
1 | /* |
2 | The software in this package is distributed under the GNU General | |
3 | Public License version 2 (with a special exception described below). | |
4 | ||
5 | A copy of GNU General Public License (GPL) is included in this distribution, | |
6 | in the file COPYING.GPL. | |
7 | ||
8 | As a special exception, if other files instantiate templates or use macros | |
9 | or inline functions from this file, or you compile this file and link it | |
10 | with other works to produce a work based on this file, this file | |
11 | does not by itself cause the resulting work to be covered | |
12 | by the GNU General Public License. | |
13 | ||
14 | However the source code for this file must still be made available | |
15 | in accordance with section (3) of the GNU General Public License. | |
16 | ||
17 | This exception does not invalidate any other reasons why a work based | |
18 | on this file might be covered by the GNU General Public License. | |
19 | */ | |
20 | ||
9a2428f9 CH |
21 | #include <fstream> |
22 | #include <ios> | |
1d110d20 | 23 | #include <logfunc.hpp> |
9a2428f9 | 24 | #include <boost/foreach.hpp> |
1d110d20 | 25 | #include <boost/program_options.hpp> |
9a2428f9 | 26 | #include <boost/asio/ip/icmp.hpp> |
1d110d20 CH |
27 | |
28 | #include "icmp/icmppacketfactory.h" | |
29 | #include "tcp/tcpsegmentfactory.h" | |
688d4b27 | 30 | #include "tools/pcap.h" |
1d110d20 CH |
31 | |
32 | using I2n::Logger::GlobalLogger; | |
33 | namespace po = boost::program_options; | |
34 | ||
9a2428f9 CH |
35 | |
36 | //------------------------------------------------------------------------------ | |
37 | // constants | |
1d110d20 CH |
38 | const bool default_is_icmp = true; |
39 | const bool default_is_v4 = true; | |
40 | ||
9a2428f9 CH |
41 | //------------------------------------------------------------------------------ |
42 | // function declarations | |
43 | void init_logger(); | |
44 | void increase_verbosity(); | |
9a2428f9 CH |
45 | int read_packets(std::istream &input_stream, const bool, const bool, const int); |
46 | ||
47 | //------------------------------------------------------------------------------ | |
48 | // helper functions | |
49 | ||
1d110d20 CH |
50 | void init_logger() |
51 | { | |
9a2428f9 | 52 | // set default: log at level NOTICE to stderr |
1d110d20 | 53 | I2n::Logger::enable_stderr_log( true ); |
0caefeeb | 54 | I2n::Logger::set_log_level( I2n::Logger::LogLevel::Info ); |
9a2428f9 | 55 | GlobalLogger.info() << "Logger initiated"; |
1d110d20 CH |
56 | } |
57 | ||
58 | void increase_verbosity() | |
59 | { | |
0caefeeb | 60 | I2n::Logger::set_log_level( I2n::Logger::LogLevel::Debug ); |
9a2428f9 | 61 | GlobalLogger.info() << "Increased verbosity"; |
1d110d20 CH |
62 | } |
63 | ||
cc59e6ff CH |
64 | /** |
65 | * @brief read packets from given input stream | |
66 | * | |
67 | * @returns positive error code or negated number of packets created | |
68 | */ | |
9a2428f9 | 69 | int read_packets( std::istream &input_stream, const bool is_icmp, |
ed614187 | 70 | const bool is_v4) |
9a2428f9 CH |
71 | { |
72 | // peek at start of stream to see if there is a pcap header | |
73 | bool is_pcap = check_for_pcap_header(input_stream); | |
688d4b27 CH |
74 | if (is_pcap) |
75 | GlobalLogger.info() << "File is pcap"; | |
76 | else | |
77 | GlobalLogger.info() << "File is not pcap"; | |
78 | ||
9a2428f9 | 79 | int next_val; |
cc59e6ff | 80 | int packet_count = 0; |
688d4b27 | 81 | uint32_t packet_type = 0; |
9a2428f9 | 82 | if (is_pcap) |
688d4b27 CH |
83 | { |
84 | packet_type = consume_pcap_file_header(input_stream); | |
85 | GlobalLogger.debug() << "Packet type from header is " << packet_type; | |
86 | } | |
9a2428f9 CH |
87 | if ( !input_stream ) |
88 | { | |
89 | GlobalLogger.notice() << "Failure consuming pcap file header!"; | |
0caefeeb | 90 | return 3; |
9a2428f9 CH |
91 | } |
92 | ||
93 | // read from stream until it is empty | |
94 | while (true) | |
95 | { | |
96 | if (is_pcap) | |
97 | { | |
98 | // assume that there is a packet header if stream had pcap header | |
688d4b27 | 99 | GlobalLogger.debug() << "Consume packet header"; |
9a2428f9 CH |
100 | consume_pcap_packet_header(input_stream); |
101 | if ( !input_stream ) | |
102 | { | |
103 | GlobalLogger.notice() <<"Failure consuming pcap packet header!"; | |
0caefeeb | 104 | return 4; |
9a2428f9 | 105 | } |
688d4b27 CH |
106 | |
107 | if (packet_type == 1) | |
108 | { | |
109 | GlobalLogger.debug() << "Consume ethernet header"; | |
110 | consume_pcap_ethernet_header(input_stream); | |
111 | if ( !input_stream ) | |
112 | { | |
113 | GlobalLogger.notice() <<"Failure consuming pcap packet header!"; | |
114 | return 5; | |
115 | } | |
116 | } | |
9a2428f9 | 117 | } |
1d110d20 | 118 | |
9a2428f9 CH |
119 | // feed data to right factory |
120 | if (is_icmp) | |
121 | { | |
122 | IcmpPacketItem packet; | |
123 | if (is_v4) | |
124 | { | |
125 | GlobalLogger.info() << "Trying to read ICMP v4 packet" | |
126 | << std::endl; | |
127 | packet = IcmpPacketFactory::create_icmp_packet( | |
ed614187 | 128 | boost::asio::ip::icmp::v4(), input_stream); |
9a2428f9 CH |
129 | } |
130 | else // v6 | |
131 | { | |
132 | GlobalLogger.info() << "Trying to read ICMP v6 packet" | |
133 | << std::endl; | |
134 | packet = IcmpPacketFactory::create_icmp_packet( | |
ed614187 | 135 | boost::asio::ip::icmp::v6(), input_stream); |
9a2428f9 CH |
136 | } |
137 | ||
138 | if (packet) | |
cc59e6ff | 139 | { |
9a2428f9 | 140 | GlobalLogger.notice() << "Succesfully created ICMP packet"; |
cc59e6ff CH |
141 | GlobalLogger.info() << packet->to_string(); |
142 | } | |
9a2428f9 CH |
143 | else |
144 | { | |
145 | GlobalLogger.notice() << "ICMP packet creation failed"; | |
688d4b27 | 146 | return 6; |
9a2428f9 CH |
147 | } |
148 | } | |
149 | else // is tcp | |
150 | { | |
151 | TcpSegmentItem segment; | |
152 | if (is_v4) | |
153 | { | |
154 | GlobalLogger.info() << "Trying to read TCP v4 packet" | |
155 | << std::endl; | |
156 | segment = TcpSegmentFactory::create_tcp_segment( | |
157 | boost::asio::ip::tcp_raw_protocol::v4(), | |
158 | input_stream); | |
159 | } | |
160 | else // v6 | |
161 | { | |
162 | GlobalLogger.info() << "Trying to read TCP v6 packet" | |
163 | << std::endl; | |
164 | segment = TcpSegmentFactory::create_tcp_segment( | |
165 | boost::asio::ip::tcp_raw_protocol::v6(), | |
166 | input_stream); | |
167 | } | |
168 | ||
169 | if (segment) | |
170 | GlobalLogger.notice() << "Succesfully created TCP segment"; | |
171 | else | |
172 | { | |
173 | GlobalLogger.notice() << "TCP segment creation failed"; | |
688d4b27 | 174 | return 6; |
9a2428f9 CH |
175 | } |
176 | } | |
177 | ||
178 | if ( !input_stream ) | |
179 | { | |
180 | GlobalLogger.notice() << "Stream not good after reading!"; | |
688d4b27 | 181 | return 7; |
9a2428f9 CH |
182 | } |
183 | ||
cc59e6ff CH |
184 | // if reached this point, created a packet, otherwise will have |
185 | // returned with error code | |
186 | ++packet_count; | |
187 | ||
9a2428f9 CH |
188 | // peek 1 byte to check whether we are at the end of stream |
189 | next_val = input_stream.get(); | |
190 | if ( input_stream.eof() ) | |
191 | { | |
192 | GlobalLogger.info() << "Reached end of stream"; | |
193 | break; | |
194 | } | |
688d4b27 CH |
195 | GlobalLogger.debug() << "Stream continues with value " << std::showbase |
196 | << std::hex << next_val; | |
197 | ||
198 | if (is_pcap && packet_type == 1 && next_val == 0) | |
199 | { | |
200 | GlobalLogger.debug() << "Consume 0s added for alignment"; | |
201 | while (input_stream.good() && next_val == 0) | |
202 | next_val = input_stream.get(); | |
203 | ||
204 | // if stream is still good, then got a non-0 value from it | |
205 | // --> put it back | |
206 | if (input_stream.good()) | |
207 | input_stream.unget(); | |
208 | else | |
209 | { | |
210 | GlobalLogger.info() << "Reached end of stream"; | |
211 | break; | |
212 | } | |
213 | } | |
214 | else | |
215 | input_stream.unget(); | |
9a2428f9 CH |
216 | } //eo: while (input_stream) |
217 | ||
cc59e6ff | 218 | return (-1) * packet_count; |
9a2428f9 CH |
219 | } // eo: function read_packets |
220 | ||
221 | //------------------------------------------------------------------------------ | |
222 | /** @brief main function | |
223 | * | |
224 | * @returns 0 if all input resulted in good packets or the error number of the | |
225 | * last failed packet | |
226 | */ | |
1d110d20 CH |
227 | int main(int argc, char *argv[]) |
228 | { | |
229 | // init logging | |
230 | init_logger(); | |
1d110d20 CH |
231 | |
232 | bool is_icmp = default_is_icmp; | |
233 | bool is_v4 = default_is_v4; | |
cc59e6ff CH |
234 | int current_return, return_val = 0; |
235 | int packet_count_good = 0; | |
236 | int packet_count_bad = 0; | |
ed614187 | 237 | DumpMode dump_mode = DUMP_NEVER; |
1d110d20 | 238 | |
9a2428f9 CH |
239 | if (argc == 1) |
240 | { | |
241 | GlobalLogger.notice() << "No arguments! Start with mix of file names " | |
242 | << "and flags"; | |
243 | GlobalLogger.notice() << "Valid flags: -i[cmp], -t[cp], -[v]4 -[v]6; " | |
244 | << "-- to read from stdin instead of file"; | |
245 | return 1; | |
246 | } | |
1d110d20 | 247 | |
ed614187 CH |
248 | IcmpPacketFactory::PacketDumpMode = dump_mode; |
249 | ||
9a2428f9 CH |
250 | // convert arguments to vector of strings and loop over them |
251 | std::vector<std::string> args(argv+1, argv + argc); | |
1d110d20 CH |
252 | BOOST_FOREACH (const std::string &arg, args) |
253 | { | |
9a2428f9 | 254 | GlobalLogger.debug() << "Parsing next arg: " << arg; |
cc59e6ff | 255 | current_return = 0; |
9a2428f9 | 256 | |
1d110d20 CH |
257 | // check if is some specification of data format |
258 | if (arg == "-i" || arg == "-icmp" || arg == "i" || arg == "icmp") | |
259 | is_icmp = true; | |
260 | else if (arg == "-t" || arg == "-tcp" || arg == "t" || arg == "tcp") | |
261 | is_icmp = false; | |
262 | else if (arg == "-4" || arg == "-v4" || arg == "4" || arg == "v4") | |
9a2428f9 | 263 | is_v4 = true; |
1d110d20 | 264 | else if (arg == "-6" || arg == "-v6" || arg == "6" || arg == "v6") |
9a2428f9 CH |
265 | is_v4 = false; |
266 | else if (arg == "-v") | |
267 | increase_verbosity(); | |
1d110d20 CH |
268 | else if (arg == "--") // read input from stdin |
269 | { | |
1d110d20 | 270 | GlobalLogger.info() << "Trying to read from stdin" << std::endl; |
ed614187 | 271 | current_return = read_packets(std::cin, is_icmp, is_v4); |
1d110d20 CH |
272 | } |
273 | else // assume is file name | |
274 | { | |
9a2428f9 | 275 | GlobalLogger.info() << "Trying to read from " << arg |
1d110d20 | 276 | << std::endl; |
9a2428f9 CH |
277 | std::ifstream file_stream(arg.c_str(), std::ios::in | |
278 | std::ios::binary); | |
0caefeeb CH |
279 | if ( !file_stream ) |
280 | { | |
281 | GlobalLogger.notice() << "Failed to open file " << arg | |
282 | << " for reading!"; | |
cc59e6ff | 283 | current_return = 2; |
0caefeeb CH |
284 | } |
285 | else | |
286 | { | |
ed614187 | 287 | current_return = read_packets( file_stream, is_icmp, is_v4); |
0caefeeb CH |
288 | file_stream.close(); |
289 | } | |
1d110d20 CH |
290 | } |
291 | ||
cc59e6ff | 292 | if (current_return > 0) |
9a2428f9 | 293 | { |
cc59e6ff CH |
294 | GlobalLogger.debug() << "Remember error value " << current_return; |
295 | return_val = current_return; | |
296 | ++packet_count_bad; | |
9a2428f9 | 297 | } |
cc59e6ff CH |
298 | else // returned the number of packets created * (-1) |
299 | packet_count_good -= current_return; | |
9a2428f9 | 300 | } //eo: loop over input-files |
1d110d20 | 301 | |
cc59e6ff CH |
302 | GlobalLogger.notice() << "Created (at least) " << packet_count_good |
303 | << " packets successfully and failed for " << packet_count_bad; | |
304 | // ("at least" because if we get an error code from read_packets, then | |
305 | // there might have been successfull reads earlier in same stream) | |
306 | ||
9a2428f9 CH |
307 | GlobalLogger.debug() << "End program with return value " << return_val; |
308 | return return_val; | |
1d110d20 | 309 | |
1d110d20 | 310 | } // eo: function main |
9a2428f9 | 311 |