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
22using Poco::TimeoutException;
23using Poco::Timespan;
24using Poco::Exception;
25
26
27namespace Poco {
28namespace Net {
29
30
31ICMPSocketImpl::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
43ICMPSocketImpl::~ICMPSocketImpl()
44{
45}
46
47
48int 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
55int 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