d11ab10cfb6be5917dd1efed2896df60f1f37e11
[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                                               const int dump_mode )
72 {
73     // peek at start of stream to see if there is a pcap header
74     bool is_pcap = check_for_pcap_header(input_stream);
75     if (is_pcap)
76         GlobalLogger.info() << "File is pcap";
77     else
78         GlobalLogger.info() << "File is not pcap";
79
80     int next_val;
81     int packet_count = 0;
82     uint32_t packet_type = 0;
83     if (is_pcap)
84     {
85         packet_type = consume_pcap_file_header(input_stream);
86         GlobalLogger.debug() << "Packet type from header is " << packet_type;
87     }
88     if ( !input_stream )
89     {
90         GlobalLogger.notice() << "Failure consuming pcap file header!";
91         return 3;
92     }
93
94     // read from stream until it is empty
95     while (true)
96     {
97         if (is_pcap)
98         {
99             // assume that there is a packet header if stream had pcap header
100             GlobalLogger.debug() << "Consume packet header";
101             consume_pcap_packet_header(input_stream);
102             if ( !input_stream )
103             {
104                 GlobalLogger.notice() <<"Failure consuming pcap packet header!";
105                 return 4;
106             }
107
108             if (packet_type == 1)
109             {
110                 GlobalLogger.debug() << "Consume ethernet header";
111                 consume_pcap_ethernet_header(input_stream);
112                 if ( !input_stream )
113                 {
114                     GlobalLogger.notice() <<"Failure consuming pcap packet header!";
115                     return 5;
116                 }
117             }
118         }
119
120         // feed data to right factory
121         if (is_icmp)
122         {
123             IcmpPacketItem packet;
124             if (is_v4)
125             {
126                 GlobalLogger.info() << "Trying to read ICMP v4 packet"
127                                     << std::endl;
128                 packet = IcmpPacketFactory::create_icmp_packet(
129                         boost::asio::ip::icmp::v4(), input_stream, dump_mode);
130             }
131             else  // v6
132             {
133                 GlobalLogger.info() << "Trying to read ICMP v6 packet"
134                                     << std::endl;
135                 packet = IcmpPacketFactory::create_icmp_packet(
136                         boost::asio::ip::icmp::v6(), input_stream, dump_mode);
137             }
138
139             if (packet)
140             {
141                 GlobalLogger.notice() << "Succesfully created ICMP packet";
142                 GlobalLogger.info() << packet->to_string();
143             }
144             else
145             {
146                 GlobalLogger.notice() << "ICMP packet creation failed";
147                 return 6;
148             }
149         }
150         else   // is tcp
151         {
152             TcpSegmentItem segment;
153             if (is_v4)
154             {
155                 GlobalLogger.info() << "Trying to read TCP v4 packet"
156                                     << std::endl;
157                 segment = TcpSegmentFactory::create_tcp_segment(
158                         boost::asio::ip::tcp_raw_protocol::v4(),
159                         input_stream);
160             }
161             else  // v6
162             {
163                 GlobalLogger.info() << "Trying to read TCP v6 packet"
164                                     << std::endl;
165                 segment = TcpSegmentFactory::create_tcp_segment(
166                         boost::asio::ip::tcp_raw_protocol::v6(),
167                         input_stream);
168             }
169
170             if (segment)
171                 GlobalLogger.notice() << "Succesfully created TCP segment";
172             else
173             {
174                 GlobalLogger.notice() << "TCP segment creation failed";
175                 return 6;
176             }
177         }
178
179         if ( !input_stream )
180         {
181             GlobalLogger.notice() << "Stream not good after reading!";
182             return 7;
183         }
184
185         // if reached this point, created a packet, otherwise will have
186         // returned with error code
187         ++packet_count;
188
189         // peek 1 byte to check whether we are at the end of stream
190         next_val = input_stream.get();
191         if ( input_stream.eof() )
192         {
193             GlobalLogger.info() << "Reached end of stream";
194             break;
195         }
196         GlobalLogger.debug() << "Stream continues with value " << std::showbase
197                              << std::hex << next_val;
198
199         if (is_pcap && packet_type == 1 && next_val == 0)
200         {
201             GlobalLogger.debug() << "Consume 0s added for alignment";
202             while (input_stream.good() && next_val == 0)
203                 next_val = input_stream.get();
204
205             // if stream is still good, then got a non-0 value from it
206             // --> put it back
207             if (input_stream.good())
208                 input_stream.unget();
209             else
210             {
211                 GlobalLogger.info() << "Reached end of stream";
212                 break;
213             }
214         }
215         else
216             input_stream.unget();
217     } //eo: while (input_stream)
218
219     return (-1) * packet_count;
220 } // eo: function read_packets
221
222 //------------------------------------------------------------------------------
223 /** @brief main function
224  *
225  * @returns 0 if all input resulted in good packets or the error number of the
226  *   last failed packet
227  */
228 int main(int argc, char *argv[])
229 {
230     // init logging
231     init_logger();
232
233     bool is_icmp = default_is_icmp;
234     bool is_v4 = default_is_v4;
235     int current_return, return_val = 0;
236     int packet_count_good = 0;
237     int packet_count_bad = 0;
238     int dump_mode = 0;  // never dump
239
240     if (argc == 1)
241     {
242         GlobalLogger.notice() << "No arguments! Start with mix of file names "
243                               << "and flags";
244         GlobalLogger.notice() << "Valid flags: -i[cmp], -t[cp], -[v]4 -[v]6; "
245                               << "-- to read from stdin instead of file";
246         return 1;
247     }
248
249     // convert arguments to vector of strings and loop over them
250     std::vector<std::string> args(argv+1, argv + argc);
251     BOOST_FOREACH (const std::string &arg, args)
252     {
253         GlobalLogger.debug() << "Parsing next arg: " << arg;
254         current_return = 0;
255
256         // check if is some specification of data format
257         if (arg == "-i" || arg == "-icmp" || arg == "i" || arg == "icmp")
258             is_icmp = true;
259         else if (arg == "-t" || arg == "-tcp" || arg == "t" || arg == "tcp")
260             is_icmp = false;
261         else if (arg == "-4" || arg == "-v4" || arg == "4" || arg == "v4")
262             is_v4 = true;
263         else if (arg == "-6" || arg == "-v6" || arg == "6" || arg == "v6")
264             is_v4 = false;
265         else if (arg == "-v")
266             increase_verbosity();
267         else if (arg == "--")   // read input from stdin
268         {
269             GlobalLogger.info() << "Trying to read from stdin" << std::endl;
270             current_return = read_packets(std::cin, is_icmp, is_v4, dump_mode);
271         }
272         else   // assume is file name
273         {
274             GlobalLogger.info() << "Trying to read from " << arg
275                                 << std::endl;
276             std::ifstream file_stream(arg.c_str(), std::ios::in |
277                                                    std::ios::binary);
278             if ( !file_stream )
279             {
280                 GlobalLogger.notice() << "Failed to open file " << arg
281                     << " for reading!";
282                 current_return = 2;
283             }
284             else
285             {
286                 current_return = read_packets( file_stream, is_icmp, is_v4,
287                                                dump_mode);
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