made nicer static variables of packet dump mode and location
[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
CH
48void dump_packet(const std::string &data)
49{
9765a83d
CH
50 // create unique file name
51 std::stringstream temp_name;
ed614187 52 temp_name << IcmpPacketFactory::DumpFilePrefix;
9765a83d
CH
53 time_t capture_time = time(0);
54 temp_name << capture_time;
55 temp_name << "_XXXXXX.pcap";
56 std::string temp_str = temp_name.str();
a92bd1ca
CH
57 std::size_t temp_size = temp_str.size();
58 boost::scoped_array<char> secure_filename( new char[temp_size + 1] );
59 std::copy(temp_str.begin(), temp_str.end(), secure_filename.get());
60 secure_filename[temp_size] = '\0';
61 int fd = mkstemps(secure_filename.get(), 5); // 5 = ".pcap".length
9765a83d
CH
62 if (fd == -1)
63 {
174abc69
CH
64 GlobalLogger.warning() << "Failed to create temp file "
65 << secure_filename.get() << ": " << strerror(errno) << "!" << endl;
66 // maybe create containing directory if errno == ENOENT?
9765a83d
CH
67 return;
68 }
69
9765a83d
CH
70 // create file pointer for file descriptor
71 FILE *fp = fdopen(fd, "w");
72
688d4b27
CH
73 // dump data
74 write_pcap_packet_data(data, fp, capture_time);
9765a83d 75
688d4b27 76 // clean up
9765a83d
CH
77 fclose(fp);
78 close(fd);
79 GlobalLogger.debug() << "Dumped a copy of the data into "
174abc69 80 << secure_filename.get() << endl;
9765a83d 81}
9765a83d 82//-----------------------------------------------------------------------------
0fb1a712
GMF
83// IcmpPacketFactory
84//-----------------------------------------------------------------------------
85
ed614187
CH
86// set default value
87DumpMode IcmpPacketFactory::PacketDumpMode = DUMP_IF_ERROR;
88std::string IcmpPacketFactory::DumpFilePrefix = "/tmp/icmp_";
89
fc3754b0 90/**
46e3c23e
GMF
91 * @brief Creates an ICMP packet from the input stream @c std::istream.
92 *
93 * @param protocol The packet's network layer protocol, IPv4 or IPv6.
94 * @param is The input stream.
95 *
96 * @return An ICMP Packet object.
fc3754b0 97 */
0fb1a712 98IcmpPacketItem IcmpPacketFactory::create_icmp_packet(
fc3754b0 99 const icmp::socket::protocol_type &protocol,
ed614187 100 istream &is
0fb1a712
GMF
101)
102{
0fb1a712
GMF
103 IcmpPacketItem icmp_packet;
104
c120ad42 105 if ( icmp::v4() == protocol || icmp::v6() == protocol )
747c13ca 106 icmp_packet.reset( new IcmpPacket( protocol ) );
fc3754b0
GMF
107 else
108 {
d9bbc1d7
CH
109 GlobalLogger.warning() << "ICMP packet creation failed: "
110 << "Unknown protocol, expect ICMP v4 or v6!" << endl;
111 icmp_packet.reset(); // --> (!icmp_packet) is true
112 return icmp_packet;
0fb1a712
GMF
113 }
114
3d5eac0b 115 IcmpPacket::ReadReturnCode return_code;
3d5eac0b 116
7238e876
CH
117 // create buffer for saving data in it
118 stringbuf data_backup;
e75a59e7 119 bool have_backup = false;
3d5eac0b 120
e75a59e7
CH
121 // create backup and parse data
122 try
7238e876 123 {
e75a59e7 124 // read packet from stream, possibly copying data first
ed614187 125 if (PacketDumpMode != DUMP_NEVER)
e75a59e7
CH
126 {
127 // read all data into backup
128 ostream backup_filler(&data_backup);
129 backup_filler << is.rdbuf();
130
131 // create a new stream from backup buffer
132 // and use that in read
133 istream is_2(&data_backup);
134 have_backup = true;
135 return_code = icmp_packet->read( is_2 );
136 }
137 else
138 return_code = icmp_packet->read( is );
139 }
140 catch ( const std::exception &ex )
141 {
142 GlobalLogger.notice() << "Exception during ICMP parse: " << ex.what()
143 << std::endl;
144 icmp_packet.reset();
145 }
146 catch ( ... )
147 {
148 GlobalLogger.notice() << "Exception during ICMP parse." << std::endl;
149 icmp_packet.reset();
3d5eac0b 150 }
3d5eac0b 151
d9bbc1d7 152 if ( return_code != IcmpPacket::ReadReturnCode_OK )
0fb1a712 153 {
d9bbc1d7
CH
154 GlobalLogger.warning() << "ICMP packet creation failed: "
155 << IcmpPacket::return_code_to_string(return_code) << endl;
04a2a163
CH
156 icmp_packet.reset(); // --> (!icmp_packet) is true
157 // --> icmp_pinger will not try to continue working with this packet
0fb1a712 158 }
d9bbc1d7
CH
159 else if ( !is.good() )
160 {
161 GlobalLogger.warning() << "ICMP packet creation failed: "
162 << "Stream not good at end of creation!" << endl;
163 icmp_packet.reset(); // --> (!icmp_packet) is true
164 }
0fb1a712 165
174abc69 166 // dump data if had trouble with packet or dumping is set to always
ed614187
CH
167 if ( PacketDumpMode == DUMP_ALWAYS ||
168 ( PacketDumpMode == DUMP_IF_ERROR && !icmp_packet ) )
e75a59e7
CH
169 {
170 if ( have_backup )
171 dump_packet(data_backup.str());
172 else
173 GlobalLogger.warning() << "Would like to dump packet but "
ed614187 174 << "trouble occured before backup was created!";
e75a59e7 175 }
3d5eac0b 176
7edd33cf
CH
177 if (icmp_packet)
178 GlobalLogger.debug() << "Read packet " << icmp_packet->to_string()
179 << endl;
180 else
181 GlobalLogger.debug() << "Read packet failed" << endl;
182
0fb1a712
GMF
183 return icmp_packet;
184}
185
fc3754b0 186/**
46e3c23e
GMF
187 * @brief Creates an ICMP Echo Request packet.
188 *
189 * @param protocol The packet's network layer protocol, IPv4 or IPv6.
190 * @param identifier The packet's identifier number to aid in matching Echo
191 * Replies to this Echo Request. May be zero.
192 * @param sequence_number The packet's sequence number to aid in matching Echo
193 * Replies to this Echo Request. May be zero.
194 *
195 * @return An ICMP Echo Request packet object.
fc3754b0 196 */
0fb1a712 197IcmpPacketItem IcmpPacketFactory::create_icmp_packet_echo_request(
fc3754b0 198 const icmp::socket::protocol_type &protocol,
0fb1a712
GMF
199 const uint16_t identifier,
200 const uint16_t sequence_number
201)
202{
fc3754b0 203 BOOST_ASSERT( (icmp::v4() == protocol) || (icmp::v6() == protocol) );
0fb1a712 204
c120ad42 205 uint8_t type;
fc3754b0 206 if ( icmp::v4() == protocol )
c120ad42 207 type = static_cast<uint8_t>(Icmpv4Type_EchoRequest);
fc3754b0 208 else if ( icmp::v6() == protocol )
c120ad42
CH
209 type = static_cast<uint8_t>(Icmpv6Type_EchoRequest);
210 // other case caught be BOOST_ASSERT above
0fb1a712 211
0fb1a712 212 uint8_t code = 0;
c120ad42 213 IcmpHeader icmp_header( type, code );
0fb1a712 214
c120ad42
CH
215 IcmpDataPtr icmp_data( new IcmpEchoData( identifier, sequence_number,
216 "ping-message" ) );
0fb1a712 217
c120ad42
CH
218 IcmpPacketItem icmp_packet( new IcmpPacket( protocol,
219 icmp_header,
220 icmp_data ) );
7edd33cf
CH
221 GlobalLogger.debug() << "IcmpPacketFactory: created echo request packet"
222 << std::endl;
0fb1a712
GMF
223
224 return icmp_packet;
225}
226