extended ICMP packet dumping to parts after packet creation
[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
GMF
35#include <logfunc.hpp>
36
780b0bca 37#include "boost_assert_handler.h"
0fb1a712 38#include "icmp/icmpdata.h"
c120ad42 39#include "icmp/icmpheader.h"
0fb1a712 40#include "icmp/icmptype.h"
c120ad42 41#include "icmp/icmpechodata.h"
688d4b27 42#include "tools/pcap.h"
0fb1a712
GMF
43
44using namespace std;
fc3754b0 45using boost::asio::ip::icmp;
0fb1a712
GMF
46using I2n::Logger::GlobalLogger;
47
9765a83d 48//-----------------------------------------------------------------------------
0fb1a712
GMF
49// IcmpPacketFactory
50//-----------------------------------------------------------------------------
51
ed614187
CH
52// set default value
53DumpMode IcmpPacketFactory::PacketDumpMode = DUMP_IF_ERROR;
54std::string IcmpPacketFactory::DumpFilePrefix = "/tmp/icmp_";
55
fc3754b0 56/**
46e3c23e
GMF
57 * @brief Creates an ICMP packet from the input stream @c std::istream.
58 *
59 * @param protocol The packet's network layer protocol, IPv4 or IPv6.
60 * @param is The input stream.
61 *
62 * @return An ICMP Packet object.
fc3754b0 63 */
0fb1a712 64IcmpPacketItem IcmpPacketFactory::create_icmp_packet(
fc3754b0 65 const icmp::socket::protocol_type &protocol,
ed614187 66 istream &is
0fb1a712
GMF
67)
68{
0fb1a712
GMF
69 IcmpPacketItem icmp_packet;
70
c120ad42 71 if ( icmp::v4() == protocol || icmp::v6() == protocol )
747c13ca 72 icmp_packet.reset( new IcmpPacket( protocol ) );
fc3754b0
GMF
73 else
74 {
d9bbc1d7
CH
75 GlobalLogger.warning() << "ICMP packet creation failed: "
76 << "Unknown protocol, expect ICMP v4 or v6!" << endl;
77 icmp_packet.reset(); // --> (!icmp_packet) is true
78 return icmp_packet;
0fb1a712
GMF
79 }
80
3d5eac0b 81 IcmpPacket::ReadReturnCode return_code;
3d5eac0b 82
7238e876
CH
83 // create buffer for saving data in it
84 stringbuf data_backup;
e75a59e7 85 bool have_backup = false;
3d5eac0b 86
e75a59e7
CH
87 // create backup and parse data
88 try
7238e876 89 {
e75a59e7 90 // read packet from stream, possibly copying data first
ed614187 91 if (PacketDumpMode != DUMP_NEVER)
e75a59e7
CH
92 {
93 // read all data into backup
94 ostream backup_filler(&data_backup);
95 backup_filler << is.rdbuf();
96
97 // create a new stream from backup buffer
98 // and use that in read
99 istream is_2(&data_backup);
100 have_backup = true;
101 return_code = icmp_packet->read( is_2 );
102 }
103 else
104 return_code = icmp_packet->read( is );
6d80c0be
CH
105
106 if ( return_code != IcmpPacket::ReadReturnCode_OK )
107 {
108 GlobalLogger.warning() << "ICMP packet creation failed: "
109 << IcmpPacket::return_code_to_string(return_code) << endl;
110 icmp_packet.reset(); // --> (!icmp_packet) is true
111 // --> icmp_pinger will not try to continue working with this packet
112 }
113 else if ( !is.good() )
114 {
115 GlobalLogger.warning() << "ICMP packet creation failed: "
116 << "Stream not good at end of creation!" << endl;
117 icmp_packet.reset(); // --> (!icmp_packet) is true
118 }
119
120 // print end result within try-catch because to_string might also
121 // throw exception
122 if (icmp_packet)
123 GlobalLogger.debug() << "Read packet " << icmp_packet->to_string()
124 << endl;
125 else
126 GlobalLogger.debug() << "Read packet failed" << endl;
127
e75a59e7
CH
128 }
129 catch ( const std::exception &ex )
130 {
131 GlobalLogger.notice() << "Exception during ICMP parse: " << ex.what()
132 << std::endl;
133 icmp_packet.reset();
134 }
135 catch ( ... )
136 {
137 GlobalLogger.notice() << "Exception during ICMP parse." << std::endl;
138 icmp_packet.reset();
3d5eac0b 139 }
3d5eac0b 140
174abc69 141 // dump data if had trouble with packet or dumping is set to always
ed614187
CH
142 if ( PacketDumpMode == DUMP_ALWAYS ||
143 ( PacketDumpMode == DUMP_IF_ERROR && !icmp_packet ) )
e75a59e7
CH
144 {
145 if ( have_backup )
146 dump_packet(data_backup.str());
147 else
148 GlobalLogger.warning() << "Would like to dump packet but "
ed614187 149 << "trouble occured before backup was created!";
e75a59e7 150 }
3d5eac0b 151
0fb1a712
GMF
152 return icmp_packet;
153}
154
fc3754b0 155/**
46e3c23e
GMF
156 * @brief Creates an ICMP Echo Request packet.
157 *
158 * @param protocol The packet's network layer protocol, IPv4 or IPv6.
159 * @param identifier The packet's identifier number to aid in matching Echo
160 * Replies to this Echo Request. May be zero.
161 * @param sequence_number The packet's sequence number to aid in matching Echo
162 * Replies to this Echo Request. May be zero.
163 *
164 * @return An ICMP Echo Request packet object.
fc3754b0 165 */
0fb1a712 166IcmpPacketItem IcmpPacketFactory::create_icmp_packet_echo_request(
fc3754b0 167 const icmp::socket::protocol_type &protocol,
0fb1a712
GMF
168 const uint16_t identifier,
169 const uint16_t sequence_number
170)
171{
fc3754b0 172 BOOST_ASSERT( (icmp::v4() == protocol) || (icmp::v6() == protocol) );
0fb1a712 173
c120ad42 174 uint8_t type;
fc3754b0 175 if ( icmp::v4() == protocol )
c120ad42 176 type = static_cast<uint8_t>(Icmpv4Type_EchoRequest);
fc3754b0 177 else if ( icmp::v6() == protocol )
c120ad42
CH
178 type = static_cast<uint8_t>(Icmpv6Type_EchoRequest);
179 // other case caught be BOOST_ASSERT above
0fb1a712 180
0fb1a712 181 uint8_t code = 0;
c120ad42 182 IcmpHeader icmp_header( type, code );
0fb1a712 183
c120ad42
CH
184 IcmpDataPtr icmp_data( new IcmpEchoData( identifier, sequence_number,
185 "ping-message" ) );
0fb1a712 186
c120ad42
CH
187 IcmpPacketItem icmp_packet( new IcmpPacket( protocol,
188 icmp_header,
189 icmp_data ) );
7edd33cf
CH
190 GlobalLogger.debug() << "IcmpPacketFactory: created echo request packet"
191 << std::endl;
0fb1a712
GMF
192
193 return icmp_packet;
194}
195
6d80c0be
CH
196
197/**
198 * @brief helper function for dump_packet methods, do not use elsewhere!
199 *
200 * creates a file descriptor and expects calling function dump_packet to deal
201 * with it (i.e. test and close); also logs a dump_packet-related text
202 *
203 * @returns file descriptor from mkstemps
204 */
205int IcmpPacketFactory::create_dump_file(const time_t &capture_time)
206{
207 std::stringstream temp_name;
208 temp_name << DumpFilePrefix;
209 temp_name << capture_time;
210 temp_name << "_XXXXXX.pcap";
211 std::string temp_str = temp_name.str();
212 std::size_t temp_size = temp_str.size();
213 boost::scoped_array<char> secure_filename( new char[temp_size + 1] );
214 std::copy(temp_str.begin(), temp_str.end(), secure_filename.get());
215 secure_filename[temp_size] = '\0';
216 int fd = mkstemps(secure_filename.get(), 5); // 5 = ".pcap".length
217 if (fd == -1)
218 GlobalLogger.warning() << "Failed to create temp file "
219 << secure_filename.get() << ": " << strerror(errno) << "!" << endl;
220 // maybe create containing directory if errno == ENOENT?
221 else
222 GlobalLogger.debug() << "Dumping a copy of the data into "
223 << secure_filename.get() << endl;
224 return fd;
225}
226
227
228void IcmpPacketFactory::dump_packet(const std::string &data)
229{
230 // create unique file name
231 time_t capture_time = time(0);
232 int fd = create_dump_file(capture_time);
233 if (fd == -1)
234 return;
235
236 // create file pointer for file descriptor
237 FILE *fp = fdopen(fd, "w");
238
239 // dump data
240 write_pcap_packet_data(data, fp, capture_time);
241
242 // clean up
243 fclose(fp);
244 close(fd);
245}
246
247void IcmpPacketFactory::dump_packet(const IcmpPacket &packet)
248{
249 // create unique file name
250 time_t capture_time = time(0);
251 int fd = create_dump_file(capture_time);
252 if (fd == -1)
253 return;
254
255 // create file pointer for file descriptor
256 FILE *fp = fdopen(fd, "w");
257
258 // create string with packet contents
259 std::stringstream data;
260 packet.dump(data);
261
262 // dump data
263 write_pcap_packet_data(data.str(), fp, capture_time);
264
265 // clean up
266 fclose(fp);
267 close(fd);
268}