made nicer static variables of packet dump mode and location
[pingcheck] / src / tools / feed_packet_data.cpp
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
21 #include <fstream>
22 #include <ios>
23 #include <logfunc.hpp>
24 #include <boost/foreach.hpp>
25 #include <boost/program_options.hpp>
26 #include <boost/asio/ip/icmp.hpp>
27
28 #include "icmp/icmppacketfactory.h"
29 #include "tcp/tcpsegmentfactory.h"
30 #include "tools/pcap.h"
31
32 using I2n::Logger::GlobalLogger;
33 namespace po = boost::program_options;
34
35
36 //------------------------------------------------------------------------------
37 // constants
38 const bool default_is_icmp = true;
39 const bool default_is_v4 = true;
40
41 //------------------------------------------------------------------------------
42 // function declarations
43 void init_logger();
44 void increase_verbosity();
45 int read_packets(std::istream &input_stream, const bool, const bool, const int);
46
47 //------------------------------------------------------------------------------
48 // helper functions
49
50 void init_logger()
51 {
52     // set default: log at level NOTICE to stderr
53     I2n::Logger::enable_stderr_log( true );
54     I2n::Logger::set_log_level( I2n::Logger::LogLevel::Info );
55     GlobalLogger.info() << "Logger initiated";
56 }
57
58 void increase_verbosity()
59 {
60     I2n::Logger::set_log_level( I2n::Logger::LogLevel::Debug );
61     GlobalLogger.info() << "Increased verbosity";
62 }
63
64 /**
65  * @brief read packets from given input stream
66  *
67  * @returns positive error code or negated number of packets created
68  */
69 int read_packets( std::istream &input_stream, const bool is_icmp,
70                                               const bool is_v4)
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);
74     if (is_pcap)
75         GlobalLogger.info() << "File is pcap";
76     else
77         GlobalLogger.info() << "File is not pcap";
78
79     int next_val;
80     int packet_count = 0;
81     uint32_t packet_type = 0;
82     if (is_pcap)
83     {
84         packet_type = consume_pcap_file_header(input_stream);
85         GlobalLogger.debug() << "Packet type from header is " << packet_type;
86     }
87     if ( !input_stream )
88     {
89         GlobalLogger.notice() << "Failure consuming pcap file header!";
90         return 3;
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
99             GlobalLogger.debug() << "Consume packet header";
100             consume_pcap_packet_header(input_stream);
101             if ( !input_stream )
102             {
103                 GlobalLogger.notice() <<"Failure consuming pcap packet header!";
104                 return 4;
105             }
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             }
117         }
118
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(
128                                      boost::asio::ip::icmp::v4(), input_stream);
129             }
130             else  // v6
131             {
132                 GlobalLogger.info() << "Trying to read ICMP v6 packet"
133                                     << std::endl;
134                 packet = IcmpPacketFactory::create_icmp_packet(
135                                      boost::asio::ip::icmp::v6(), input_stream);
136             }
137
138             if (packet)
139             {
140                 GlobalLogger.notice() << "Succesfully created ICMP packet";
141                 GlobalLogger.info() << packet->to_string();
142             }
143             else
144             {
145                 GlobalLogger.notice() << "ICMP packet creation failed";
146                 return 6;
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";
174                 return 6;
175             }
176         }
177
178         if ( !input_stream )
179         {
180             GlobalLogger.notice() << "Stream not good after reading!";
181             return 7;
182         }
183
184         // if reached this point, created a packet, otherwise will have
185         // returned with error code
186         ++packet_count;
187
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         }
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();
216     } //eo: while (input_stream)
217
218     return (-1) * packet_count;
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  */
227 int main(int argc, char *argv[])
228 {
229     // init logging
230     init_logger();
231
232     bool is_icmp = default_is_icmp;
233     bool is_v4 = default_is_v4;
234     int current_return, return_val = 0;
235     int packet_count_good = 0;
236     int packet_count_bad = 0;
237     DumpMode dump_mode = DUMP_NEVER;
238
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     }
247
248     IcmpPacketFactory::PacketDumpMode = dump_mode;
249
250     // convert arguments to vector of strings and loop over them
251     std::vector<std::string> args(argv+1, argv + argc);
252     BOOST_FOREACH (const std::string &arg, args)
253     {
254         GlobalLogger.debug() << "Parsing next arg: " << arg;
255         current_return = 0;
256
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")
263             is_v4 = true;
264         else if (arg == "-6" || arg == "-v6" || arg == "6" || arg == "v6")
265             is_v4 = false;
266         else if (arg == "-v")
267             increase_verbosity();
268         else if (arg == "--")   // read input from stdin
269         {
270             GlobalLogger.info() << "Trying to read from stdin" << std::endl;
271             current_return = read_packets(std::cin, is_icmp, is_v4);
272         }
273         else   // assume is file name
274         {
275             GlobalLogger.info() << "Trying to read from " << arg
276                                 << std::endl;
277             std::ifstream file_stream(arg.c_str(), std::ios::in |
278                                                    std::ios::binary);
279             if ( !file_stream )
280             {
281                 GlobalLogger.notice() << "Failed to open file " << arg
282                     << " for reading!";
283                 current_return = 2;
284             }
285             else
286             {
287                 current_return = read_packets( file_stream, is_icmp, is_v4);
288                 file_stream.close();
289             }
290         }
291
292         if (current_return > 0)
293         {
294             GlobalLogger.debug() << "Remember error value " << current_return;
295             return_val = current_return;
296             ++packet_count_bad;
297         }
298         else // returned the number of packets created * (-1)
299             packet_count_good -= current_return;
300     } //eo: loop over input-files
301
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
307     GlobalLogger.debug() << "End program with return value " << return_val;
308     return return_val;
309
310 } // eo: function main
311