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 | 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 | |
45 | using namespace std; | |
fc3754b0 | 46 | using boost::asio::ip::icmp; |
0fb1a712 GMF |
47 | using I2n::Logger::GlobalLogger; |
48 | ||
9765a83d | 49 | //----------------------------------------------------------------------------- |
0fb1a712 GMF |
50 | // IcmpPacketFactory |
51 | //----------------------------------------------------------------------------- | |
52 | ||
ed614187 CH |
53 | // set default value |
54 | DumpMode IcmpPacketFactory::PacketDumpMode = DUMP_IF_ERROR; | |
55 | std::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 | 65 | IcmpPacketItem 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 | 167 | IcmpPacketItem 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 |
198 | void 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 | ||
223 | void 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 | } |