started parallel pings, not quite done yet since need to delay them
[pingcheck] / src / icmp / icmppacketfactory.cpp
CommitLineData
0fb1a712
GMF
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.
057e6cc9
CH
19
20 Christian Herdtweck, Intra2net AG 2015
21 Based on an example in Boost Documentation (by Christopher M. Kohlhoff)
22 and adaptation by Guilherme M. Ferreira
0fb1a712
GMF
23 */
24
25#include "icmp/icmppacketfactory.h"
26
9765a83d 27// for dumping packets
174abc69
CH
28#include <cstring>
29#include <cstdlib>
9765a83d 30#include <ctime>
7238e876
CH
31#include <iostream>
32#include <sstream>
a92bd1ca 33#include <boost/scoped_array.hpp>
3d5eac0b 34
0fb1a712 35#include <logfunc.hpp>
d291ad16 36#include <tmpfstream.hpp>
0fb1a712 37
780b0bca 38#include "boost_assert_handler.h"
0fb1a712 39#include "icmp/icmpdata.h"
c120ad42 40#include "icmp/icmpheader.h"
0fb1a712 41#include "icmp/icmptype.h"
c120ad42 42#include "icmp/icmpechodata.h"
688d4b27 43#include "tools/pcap.h"
0fb1a712
GMF
44
45using namespace std;
fc3754b0 46using boost::asio::ip::icmp;
0fb1a712
GMF
47using I2n::Logger::GlobalLogger;
48
9765a83d 49//-----------------------------------------------------------------------------
0fb1a712
GMF
50// IcmpPacketFactory
51//-----------------------------------------------------------------------------
52
ed614187
CH
53// set default value
54DumpMode IcmpPacketFactory::PacketDumpMode = DUMP_IF_ERROR;
55std::string IcmpPacketFactory::DumpFilePrefix = "/tmp/icmp_";
56
fc3754b0 57/**
46e3c23e
GMF
58 * @brief Creates an ICMP packet from the input stream @c std::istream.
59 *
60 * @param protocol The packet's network layer protocol, IPv4 or IPv6.
61 * @param is The input stream.
62 *
63 * @return An ICMP Packet object.
fc3754b0 64 */
0fb1a712 65IcmpPacketItem IcmpPacketFactory::create_icmp_packet(
fc3754b0 66 const icmp::socket::protocol_type &protocol,
ed614187 67 istream &is
0fb1a712
GMF
68)
69{
0fb1a712
GMF
70 IcmpPacketItem icmp_packet;
71
c120ad42 72 if ( icmp::v4() == protocol || icmp::v6() == protocol )
747c13ca 73 icmp_packet.reset( new IcmpPacket( protocol ) );
fc3754b0
GMF
74 else
75 {
d9bbc1d7 76 GlobalLogger.warning() << "ICMP packet creation failed: "
91aa83f9 77 << "Unknown protocol arg, expect ICMP v4 or v6!" << endl;
d9bbc1d7
CH
78 icmp_packet.reset(); // --> (!icmp_packet) is true
79 return icmp_packet;
0fb1a712
GMF
80 }
81
3d5eac0b 82 IcmpPacket::ReadReturnCode return_code;
3d5eac0b 83
7238e876
CH
84 // create buffer for saving data in it
85 stringbuf data_backup;
e75a59e7 86 bool have_backup = false;
3d5eac0b 87
e75a59e7
CH
88 // create backup and parse data
89 try
7238e876 90 {
e75a59e7 91 // read packet from stream, possibly copying data first
ed614187 92 if (PacketDumpMode != DUMP_NEVER)
e75a59e7
CH
93 {
94 // read all data into backup
95 ostream backup_filler(&data_backup);
96 backup_filler << is.rdbuf();
97
98 // create a new stream from backup buffer
99 // and use that in read
100 istream is_2(&data_backup);
101 have_backup = true;
102 return_code = icmp_packet->read( is_2 );
103 }
104 else
105 return_code = icmp_packet->read( is );
6d80c0be
CH
106
107 if ( return_code != IcmpPacket::ReadReturnCode_OK )
108 {
109 GlobalLogger.warning() << "ICMP packet creation failed: "
110 << IcmpPacket::return_code_to_string(return_code) << endl;
111 icmp_packet.reset(); // --> (!icmp_packet) is true
112 // --> icmp_pinger will not try to continue working with this packet
113 }
114 else if ( !is.good() )
115 {
116 GlobalLogger.warning() << "ICMP packet creation failed: "
117 << "Stream not good at end of creation!" << endl;
118 icmp_packet.reset(); // --> (!icmp_packet) is true
119 }
120
121 // print end result within try-catch because to_string might also
122 // throw exception
123 if (icmp_packet)
124 GlobalLogger.debug() << "Read packet " << icmp_packet->to_string()
125 << endl;
126 else
127 GlobalLogger.debug() << "Read packet failed" << endl;
128
e75a59e7
CH
129 }
130 catch ( const std::exception &ex )
131 {
132 GlobalLogger.notice() << "Exception during ICMP parse: " << ex.what()
133 << std::endl;
134 icmp_packet.reset();
135 }
136 catch ( ... )
137 {
138 GlobalLogger.notice() << "Exception during ICMP parse." << std::endl;
139 icmp_packet.reset();
3d5eac0b 140 }
3d5eac0b 141
174abc69 142 // dump data if had trouble with packet or dumping is set to always
ed614187
CH
143 if ( PacketDumpMode == DUMP_ALWAYS ||
144 ( PacketDumpMode == DUMP_IF_ERROR && !icmp_packet ) )
e75a59e7
CH
145 {
146 if ( have_backup )
147 dump_packet(data_backup.str());
148 else
149 GlobalLogger.warning() << "Would like to dump packet but "
ed614187 150 << "trouble occured before backup was created!";
e75a59e7 151 }
3d5eac0b 152
0fb1a712
GMF
153 return icmp_packet;
154}
155
fc3754b0 156/**
46e3c23e
GMF
157 * @brief Creates an ICMP Echo Request packet.
158 *
159 * @param protocol The packet's network layer protocol, IPv4 or IPv6.
160 * @param identifier The packet's identifier number to aid in matching Echo
161 * Replies to this Echo Request. May be zero.
162 * @param sequence_number The packet's sequence number to aid in matching Echo
163 * Replies to this Echo Request. May be zero.
164 *
165 * @return An ICMP Echo Request packet object.
fc3754b0 166 */
0fb1a712 167IcmpPacketItem IcmpPacketFactory::create_icmp_packet_echo_request(
fc3754b0 168 const icmp::socket::protocol_type &protocol,
0fb1a712
GMF
169 const uint16_t identifier,
170 const uint16_t sequence_number
171)
172{
fc3754b0 173 BOOST_ASSERT( (icmp::v4() == protocol) || (icmp::v6() == protocol) );
0fb1a712 174
c120ad42 175 uint8_t type;
fc3754b0 176 if ( icmp::v4() == protocol )
c120ad42 177 type = static_cast<uint8_t>(Icmpv4Type_EchoRequest);
fc3754b0 178 else if ( icmp::v6() == protocol )
c120ad42
CH
179 type = static_cast<uint8_t>(Icmpv6Type_EchoRequest);
180 // other case caught be BOOST_ASSERT above
0fb1a712 181
0fb1a712 182 uint8_t code = 0;
c120ad42 183 IcmpHeader icmp_header( type, code );
0fb1a712 184
c120ad42
CH
185 IcmpDataPtr icmp_data( new IcmpEchoData( identifier, sequence_number,
186 "ping-message" ) );
0fb1a712 187
c120ad42
CH
188 IcmpPacketItem icmp_packet( new IcmpPacket( protocol,
189 icmp_header,
190 icmp_data ) );
7edd33cf
CH
191 GlobalLogger.debug() << "IcmpPacketFactory: created echo request packet"
192 << std::endl;
0fb1a712
GMF
193
194 return icmp_packet;
195}
196
6d80c0be 197
6d80c0be
CH
198void IcmpPacketFactory::dump_packet(const std::string &data)
199{
200 // create unique file name
201 time_t capture_time = time(0);
d291ad16
CH
202 std::stringstream temp_name;
203 temp_name << DumpFilePrefix << capture_time << ".pcap.XXXXXX";
6d80c0be 204
d291ad16
CH
205 // open file
206 I2n::tmpfstream temp_stream;
207 if ( !temp_stream.open(temp_name.str()) )
208 {
209 GlobalLogger.warning() << "Failed to create temp file "
210 << temp_name.str() << ": " << strerror(errno) << "!" << endl;
211 return;
212 }
6d80c0be 213
d291ad16
CH
214 // write
215 write_pcap_packet_data(data, temp_stream, capture_time);
6d80c0be 216
d291ad16
CH
217 // close
218 GlobalLogger.debug() << "Dumped a copy of the ICMP data into "
219 << temp_stream.get_tmp_filename() << endl;
220 temp_stream.close();
6d80c0be
CH
221}
222
223void IcmpPacketFactory::dump_packet(const IcmpPacket &packet)
224{
225 // create unique file name
226 time_t capture_time = time(0);
d291ad16
CH
227 std::stringstream temp_name;
228 temp_name << DumpFilePrefix << capture_time << ".pcap.XXXXXX";
6d80c0be 229
d291ad16
CH
230 // open file
231 I2n::tmpfstream temp_stream;
232 if ( !temp_stream.open(temp_name.str()) )
233 {
234 GlobalLogger.warning() << "Failed to create temp file "
235 << temp_name.str() << ": " << strerror(errno) << "!" << endl;
236 return;
237 }
6d80c0be
CH
238
239 // dump data
d291ad16 240 packet.dump(temp_stream);
6d80c0be 241
d291ad16
CH
242 // close
243 GlobalLogger.debug() << "Dumped a copy of the packet into "
244 << temp_stream.get_tmp_filename() << endl;
245 temp_stream.close();
6d80c0be 246}