1 | // |
2 | // ICMPv4PacketImpl.cpp |
3 | // |
4 | // Library: Net |
5 | // Package: ICMP |
6 | // Module: ICMPv4PacketImpl |
7 | // |
8 | // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. |
9 | // and Contributors. |
10 | // |
11 | // SPDX-License-Identifier: BSL-1.0 |
12 | // |
13 | |
14 | |
15 | #include "Poco/Net/ICMPv4PacketImpl.h" |
16 | #include "Poco/Net/NetException.h" |
17 | #include "Poco/Timestamp.h" |
18 | #include "Poco/Timespan.h" |
19 | #include "Poco/NumberFormatter.h" |
20 | #if !defined(POCO_VXWORKS) |
21 | #include "Poco/Process.h" |
22 | #endif |
23 | #include <sstream> |
24 | |
25 | |
26 | using Poco::InvalidArgumentException; |
27 | using Poco::Timestamp; |
28 | using Poco::Timespan; |
29 | using Poco::NumberFormatter; |
30 | using Poco::UInt8; |
31 | using Poco::UInt16; |
32 | using Poco::Int32; |
33 | |
34 | |
35 | namespace Poco { |
36 | namespace Net { |
37 | |
38 | |
39 | const UInt8 ICMPv4PacketImpl::DESTINATION_UNREACHABLE_TYPE = 3; |
40 | const Poco::UInt8 ICMPv4PacketImpl::SOURCE_QUENCH_TYPE = 4; |
41 | const Poco::UInt8 ICMPv4PacketImpl::REDIRECT_MESSAGE_TYPE = 5; |
42 | const UInt8 ICMPv4PacketImpl::TIME_EXCEEDED_TYPE = 11; |
43 | const Poco::UInt8 ICMPv4PacketImpl::PARAMETER_PROBLEM_TYPE = 12; |
44 | |
45 | |
46 | const std::string ICMPv4PacketImpl::MESSAGE_TYPE[] = |
47 | { |
48 | "Echo Reply" , |
49 | "ICMP 1" , |
50 | "ICMP 2" , |
51 | "Dest Unreachable" , |
52 | "Source Quench" , |
53 | "Redirect" , |
54 | "ICMP 6" , |
55 | "ICMP 7" , |
56 | "Echo" , |
57 | "ICMP 9" , |
58 | "ICMP 10" , |
59 | "Time Exceeded" , |
60 | "Parameter Problem" , |
61 | "Timestamp" , |
62 | "Timestamp Reply" , |
63 | "Info Request" , |
64 | "Info Reply" , |
65 | "Unknown type" |
66 | }; |
67 | |
68 | |
69 | const std::string ICMPv4PacketImpl::DESTINATION_UNREACHABLE_CODE[] = |
70 | { |
71 | "Net unreachable" , |
72 | "Host unreachable" , |
73 | "Protocol unreachable" , |
74 | "Port unreachable" , |
75 | "Fragmentation needed and DF set" , |
76 | "Source route failed" , |
77 | "Unknown code" |
78 | }; |
79 | |
80 | |
81 | const std::string ICMPv4PacketImpl::REDIRECT_MESSAGE_CODE[] = |
82 | { |
83 | "Redirect datagrams for the network" , |
84 | "Redirect datagrams for the host" , |
85 | "Redirect datagrams for the type of service and network" , |
86 | "Redirect datagrams for the type of service and host" , |
87 | "Unknown code" |
88 | }; |
89 | |
90 | |
91 | const std::string ICMPv4PacketImpl::TIME_EXCEEDED_CODE[] = |
92 | { |
93 | "Time to live exceeded in transit" , |
94 | "Fragment reassembly time exceeded" , |
95 | "Unknown code" |
96 | }; |
97 | |
98 | |
99 | const std::string ICMPv4PacketImpl::PARAMETER_PROBLEM_CODE[] = |
100 | { |
101 | "Pointer indicates the error" , |
102 | "Unknown code" |
103 | }; |
104 | |
105 | |
106 | ICMPv4PacketImpl::ICMPv4PacketImpl(int dataSize) |
107 | : ICMPPacketImpl(dataSize), |
108 | _seq(0) |
109 | { |
110 | initPacket(); |
111 | } |
112 | |
113 | |
114 | ICMPv4PacketImpl::~ICMPv4PacketImpl() |
115 | { |
116 | } |
117 | |
118 | |
119 | int ICMPv4PacketImpl::packetSize() const |
120 | { |
121 | return getDataSize() + sizeof(Header); |
122 | } |
123 | |
124 | |
125 | void ICMPv4PacketImpl::initPacket() |
126 | { |
127 | if (_seq >= MAX_SEQ_VALUE) resetSequence(); |
128 | |
129 | Header* icp = (Header*) packet(false); |
130 | icp->type = ECHO_REQUEST; |
131 | icp->code = 0; |
132 | icp->checksum = 0; |
133 | icp->seq = ++_seq; |
134 | #if defined(POCO_VXWORKS) |
135 | icp->id = 0; |
136 | #else |
137 | icp->id = static_cast<UInt16>(Poco::Process::id()); |
138 | #endif |
139 | |
140 | struct timeval* ptp = (struct timeval *) (icp + 1); |
141 | *ptp = time(); |
142 | |
143 | icp->checksum = checksum((UInt16*) icp, getDataSize() + sizeof(Header)); |
144 | } |
145 | |
146 | |
147 | struct timeval ICMPv4PacketImpl::time(Poco::UInt8* buffer, int length) const |
148 | { |
149 | struct timeval tv; |
150 | |
151 | if (0 == buffer || 0 == length) |
152 | { |
153 | Timespan value(Timestamp().epochMicroseconds()); |
154 | tv.tv_sec = (long) value.totalSeconds(); |
155 | tv.tv_usec = (long) value.useconds(); |
156 | } |
157 | else |
158 | { |
159 | struct timeval* ptv = (struct timeval*) data(buffer, length); |
160 | if (ptv) tv = *ptv; |
161 | else throw InvalidArgumentException("Invalid packet." ); |
162 | } |
163 | return tv; |
164 | } |
165 | |
166 | |
167 | ICMPv4PacketImpl::Header* ICMPv4PacketImpl::(Poco::UInt8* buffer, int length) const |
168 | { |
169 | poco_check_ptr (buffer); |
170 | |
171 | int offset = (buffer[0] & 0x0F) * 4; |
172 | if ((offset + sizeof(Header)) > length) return 0; |
173 | |
174 | buffer += offset; |
175 | return (Header *) buffer; |
176 | } |
177 | |
178 | |
179 | Poco::UInt8* ICMPv4PacketImpl::data(Poco::UInt8* buffer, int length) const |
180 | { |
181 | return ((Poco::UInt8*) header(buffer, length)) + sizeof(Header); |
182 | } |
183 | |
184 | |
185 | bool ICMPv4PacketImpl::validReplyID(Poco::UInt8* buffer, int length) const |
186 | { |
187 | Header *icp = header(buffer, length); |
188 | #if defined(POCO_VXWORKS) |
189 | return icp && icp->id == 0; |
190 | #else |
191 | return icp && (static_cast<Poco::UInt16>(Process::id()) == icp->id); |
192 | #endif |
193 | } |
194 | |
195 | |
196 | std::string ICMPv4PacketImpl::errorDescription(unsigned char* buffer, int length) |
197 | { |
198 | Header *icp = header(buffer, length); |
199 | |
200 | if (!icp) return "Invalid header." ; |
201 | if (ECHO_REPLY == icp->type) return std::string(); // not an error |
202 | |
203 | UInt8 pointer = 0; |
204 | if (PARAMETER_PROBLEM == icp->type) |
205 | { |
206 | UInt8 mask = 0x00FF; |
207 | pointer = icp->id & mask; |
208 | } |
209 | |
210 | MessageType type = static_cast<MessageType>(icp->type); |
211 | int code = icp->code; |
212 | std::ostringstream err; |
213 | |
214 | switch (type) |
215 | { |
216 | case DESTINATION_UNREACHABLE_TYPE: |
217 | if (code >= NET_UNREACHABLE && code < DESTINATION_UNREACHABLE_UNKNOWN) |
218 | err << DESTINATION_UNREACHABLE_CODE[code]; |
219 | else |
220 | err << DESTINATION_UNREACHABLE_CODE[DESTINATION_UNREACHABLE_UNKNOWN]; |
221 | break; |
222 | |
223 | case SOURCE_QUENCH_TYPE: |
224 | err << "Source quench" ; |
225 | break; |
226 | |
227 | case REDIRECT_MESSAGE_TYPE: |
228 | if (code >= REDIRECT_NETWORK && code < REDIRECT_MESSAGE_UNKNOWN) |
229 | err << REDIRECT_MESSAGE_CODE[code]; |
230 | else |
231 | err << REDIRECT_MESSAGE_CODE[REDIRECT_MESSAGE_UNKNOWN]; |
232 | break; |
233 | |
234 | case TIME_EXCEEDED_TYPE: |
235 | if (code >= TIME_TO_LIVE && code < TIME_EXCEEDED_UNKNOWN) |
236 | err << TIME_EXCEEDED_CODE[code]; |
237 | else |
238 | err << TIME_EXCEEDED_CODE[TIME_EXCEEDED_UNKNOWN]; |
239 | break; |
240 | |
241 | case PARAMETER_PROBLEM_TYPE: |
242 | if (POINTER_INDICATES_THE_ERROR != code) |
243 | code = PARAMETER_PROBLEM_UNKNOWN; |
244 | err << PARAMETER_PROBLEM_CODE[code] << ": error in octet #" << pointer; |
245 | break; |
246 | |
247 | default: |
248 | err << "Unknown type." ; |
249 | break; |
250 | } |
251 | |
252 | return err.str(); |
253 | } |
254 | |
255 | std::string ICMPv4PacketImpl::typeDescription(int typeId) |
256 | { |
257 | poco_assert (typeId >= ECHO_REPLY && typeId < MESSAGE_TYPE_LENGTH); |
258 | |
259 | return MESSAGE_TYPE[typeId]; |
260 | } |
261 | |
262 | |
263 | } } // namespace Poco::Net |
264 | |