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 CH |
48 | void 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 |
87 | DumpMode IcmpPacketFactory::PacketDumpMode = DUMP_IF_ERROR; | |
88 | std::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 | 98 | IcmpPacketItem 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 | 197 | IcmpPacketItem 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 |