| 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, int& type, int& code) |
| 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 | type = icp->type; |
| 211 | MessageType msgType = static_cast<MessageType>(type); |
| 212 | code = icp->code; |
| 213 | std::ostringstream err; |
| 214 | |
| 215 | switch (msgType) |
| 216 | { |
| 217 | case DESTINATION_UNREACHABLE_TYPE: |
| 218 | if (code >= NET_UNREACHABLE && code < DESTINATION_UNREACHABLE_UNKNOWN) |
| 219 | err << DESTINATION_UNREACHABLE_CODE[code]; |
| 220 | else |
| 221 | err << DESTINATION_UNREACHABLE_CODE[DESTINATION_UNREACHABLE_UNKNOWN]; |
| 222 | break; |
| 223 | |
| 224 | case SOURCE_QUENCH_TYPE: |
| 225 | err << "Source quench" ; |
| 226 | break; |
| 227 | |
| 228 | case REDIRECT_MESSAGE_TYPE: |
| 229 | if (code >= REDIRECT_NETWORK && code < REDIRECT_MESSAGE_UNKNOWN) |
| 230 | err << REDIRECT_MESSAGE_CODE[code]; |
| 231 | else |
| 232 | err << REDIRECT_MESSAGE_CODE[REDIRECT_MESSAGE_UNKNOWN]; |
| 233 | break; |
| 234 | |
| 235 | case TIME_EXCEEDED_TYPE: |
| 236 | if (code >= TIME_TO_LIVE && code < TIME_EXCEEDED_UNKNOWN) |
| 237 | err << TIME_EXCEEDED_CODE[code]; |
| 238 | else |
| 239 | err << TIME_EXCEEDED_CODE[TIME_EXCEEDED_UNKNOWN]; |
| 240 | break; |
| 241 | |
| 242 | case PARAMETER_PROBLEM_TYPE: |
| 243 | if (POINTER_INDICATES_THE_ERROR != code) |
| 244 | code = PARAMETER_PROBLEM_UNKNOWN; |
| 245 | err << PARAMETER_PROBLEM_CODE[code] << ": error in octet #" << pointer; |
| 246 | break; |
| 247 | |
| 248 | default: |
| 249 | err << "Unknown type." ; |
| 250 | break; |
| 251 | } |
| 252 | |
| 253 | return err.str(); |
| 254 | } |
| 255 | |
| 256 | std::string ICMPv4PacketImpl::typeDescription(int typeId) |
| 257 | { |
| 258 | poco_assert (typeId >= ECHO_REPLY && typeId < MESSAGE_TYPE_LENGTH); |
| 259 | |
| 260 | return MESSAGE_TYPE[typeId]; |
| 261 | } |
| 262 | |
| 263 | |
| 264 | } } // namespace Poco::Net |
| 265 | |