1 | // |
2 | // ICMPSocketImpl.cpp |
3 | // |
4 | // Library: Net |
5 | // Package: ICMP |
6 | // Module: ICMPSocketImpl |
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/ICMPSocketImpl.h" |
16 | #include "Poco/Net/ICMPv4PacketImpl.h" |
17 | #include "Poco/Net/NetException.h" |
18 | #include "Poco/Format.h" |
19 | #include "Poco/Buffer.h" |
20 | |
21 | |
22 | using Poco::TimeoutException; |
23 | using Poco::Timespan; |
24 | using Poco::Exception; |
25 | |
26 | |
27 | namespace Poco { |
28 | namespace Net { |
29 | |
30 | |
31 | ICMPSocketImpl::ICMPSocketImpl(IPAddress::Family family, int dataSize, int ttl, int timeout): |
32 | RawSocketImpl(family, IPPROTO_ICMP), |
33 | _icmpPacket(family, dataSize), |
34 | _ttl(ttl), |
35 | _timeout(timeout) |
36 | { |
37 | setOption(IPPROTO_IP, IP_TTL, ttl); |
38 | setBlocking(true); |
39 | setReceiveTimeout(Timespan(timeout)); |
40 | } |
41 | |
42 | |
43 | ICMPSocketImpl::~ICMPSocketImpl() |
44 | { |
45 | } |
46 | |
47 | |
48 | int ICMPSocketImpl::sendTo(const void*, int, const SocketAddress& address, int flags) |
49 | { |
50 | int n = SocketImpl::sendTo(_icmpPacket.packet(), _icmpPacket.packetSize(), address, flags); |
51 | return n; |
52 | } |
53 | |
54 | |
55 | int ICMPSocketImpl::receiveFrom(void*, int, SocketAddress& address, int flags) |
56 | { |
57 | int maxPacketSize = _icmpPacket.maxPacketSize(); |
58 | Poco::Buffer<unsigned char> buffer(maxPacketSize); |
59 | |
60 | try |
61 | { |
62 | Poco::Timestamp ts; |
63 | int rc; |
64 | int expected = _icmpPacket.packetSize(); |
65 | do |
66 | { |
67 | // guard against a DoS attack |
68 | if (ts.isElapsed(_timeout)) throw TimeoutException(); |
69 | buffer.clear(); |
70 | SocketAddress respAddr; |
71 | rc = SocketImpl::receiveFrom(buffer.begin(), maxPacketSize, respAddr, flags); |
72 | if (rc == 0) break; |
73 | if (respAddr == address) |
74 | { |
75 | expected -= rc; |
76 | if (expected <= 0) |
77 | { |
78 | if (_icmpPacket.validReplyID(buffer.begin(), maxPacketSize)) break; |
79 | int type = 0, code = 0; |
80 | std::string err = _icmpPacket.errorDescription(buffer.begin(), maxPacketSize, type, code); |
81 | if (!err.empty()) throw ICMPException(err); |
82 | throw ICMPException("Invalid ICMP reply" ); |
83 | } |
84 | } |
85 | else |
86 | { |
87 | throw ICMPException(Poco::format("Reply from an unknown IP address " |
88 | "(requested %s, received %s)." , |
89 | address.host().toString(), respAddr.host().toString())); |
90 | } |
91 | } |
92 | while (expected && !_icmpPacket.validReplyID(buffer.begin(), maxPacketSize)); |
93 | } |
94 | catch (ICMPException&) |
95 | { |
96 | throw; |
97 | } |
98 | catch (TimeoutException&) |
99 | { |
100 | throw; |
101 | } |
102 | catch (Exception&) |
103 | { |
104 | int type = 0, code = 0; |
105 | std::string err = _icmpPacket.errorDescription(buffer.begin(), maxPacketSize, type, code); |
106 | if (!err.empty()) |
107 | { |
108 | if (address.family() == IPAddress::IPv4 && |
109 | type == ICMPv4PacketImpl::DESTINATION_UNREACHABLE && |
110 | code == ICMPv4PacketImpl::FRAGMENTATION_NEEDED_AND_DF_SET) |
111 | { |
112 | throw ICMPFragmentationException(err); |
113 | } |
114 | else throw ICMPException(err); |
115 | } |
116 | else |
117 | throw; |
118 | } |
119 | |
120 | struct timeval then = _icmpPacket.time(buffer.begin(), maxPacketSize); |
121 | struct timeval now = _icmpPacket.time(); |
122 | |
123 | int elapsed = (((now.tv_sec * 1000000) + now.tv_usec) - ((then.tv_sec * 1000000) + then.tv_usec))/1000; |
124 | |
125 | return elapsed; |
126 | } |
127 | |
128 | |
129 | } } // namespace Poco::Net |
130 | |