Commit | Line | Data |
---|---|---|
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 | |
44 | using namespace std; | |
fc3754b0 | 45 | using boost::asio::ip::icmp; |
0fb1a712 GMF |
46 | using I2n::Logger::GlobalLogger; |
47 | ||
9765a83d | 48 | //----------------------------------------------------------------------------- |
0fb1a712 GMF |
49 | // IcmpPacketFactory |
50 | //----------------------------------------------------------------------------- | |
51 | ||
ed614187 CH |
52 | // set default value |
53 | DumpMode IcmpPacketFactory::PacketDumpMode = DUMP_IF_ERROR; | |
54 | std::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 | 64 | IcmpPacketItem 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 | 166 | IcmpPacketItem 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 | */ | |
205 | int 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 | ||
228 | void 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 | ||
247 | void 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 | } |