Document return values
[ACE_TAO.git] / ACE / ace / Ping_Socket.cpp
blobd806a30b1eef3971288925a7ca3832ddbac3530b
1 #include "ace/Ping_Socket.h"
3 #if defined (ACE_HAS_ICMP_SUPPORT) && (ACE_HAS_ICMP_SUPPORT == 1)
5 #include "ace/INET_Addr.h"
6 #include "ace/Log_Category.h"
7 #include "ace/OS_NS_string.h"
8 #include "ace/OS_NS_sys_time.h"
9 #include "ace/OS_NS_sys_socket.h"
10 #include "ace/OS_NS_unistd.h"
11 #if defined (ACE_HAS_ALLOC_HOOKS)
12 # include "ace/Malloc_Base.h"
13 #endif /* ACE_HAS_ALLOC_HOOKS */
15 #if !defined (__ACE_INLINE__)
16 # include "ace/Ping_Socket.inl"
17 #endif /* !__ACE_INLINE__ */
19 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
21 ACE_ALLOC_HOOK_DEFINE (ACE_Ping_Socket)
23 ACE_END_VERSIONED_NAMESPACE_DECL
25 //---------------------------------------------------------------------------
26 // Better to arrange some os_include/netinet/ip.h and
27 // os_include/netinet/icmp.h files ?
28 //---------------------------------------------------------------------------
30 #if !defined (ACE_WIN32)
33 * This is where ICMP-related stuff is defined on any sane system...
35 #include /**/ <netinet/in_systm.h>
36 #include /**/ <netinet/ip.h>
37 #include /**/ <netinet/ip_icmp.h>
39 #else /* #if ! defined (ACE_WIN32) */
42 * This was a surprise to me... This stuff is not defined anywhere under MSVC.
43 * These values have only been checked for NT4 and Win2K. They were taken from
44 * the MSDN ping.c program and modified.
47 #define ICMP_ECHO 8
48 #define ICMP_ECHOREPLY 0
50 #pragma pack(1)
52 struct ip
54 ACE_UINT8 ip_hl:4; // length of the header
55 ACE_UINT8 version:4; // Version of IP
56 ACE_UINT8 tos; // Type of service
57 ACE_UINT16 total_len; // total length of the packet
58 ACE_UINT16 ident; // unique identifier
59 ACE_UINT16 frag_and_flags; // flags
60 ACE_UINT8 ip_ttl; // Time to live
61 ACE_UINT8 proto; // protocol (TCP, UDP etc)
62 ACE_UINT16 checksum; // IP checksum
63 ACE_UINT32 sourceIP;
64 ACE_UINT32 destIP;
67 struct icmp
69 ACE_UINT8 icmp_type;
70 ACE_UINT8 icmp_code; // type sub code
71 ACE_UINT16 icmp_cksum;
72 ACE_UINT16 icmp_id;
73 ACE_UINT16 icmp_seq;
74 ACE_UINT32 icmp_data; // time data
77 #pragma pack()
79 #endif /* #if ! defined (ACE_WIN32) */
82 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
84 int const ICMP_MIN = 8; // Minimal size of ICMP packet, header only
85 int const ICMP_DATA_LENGTH = 56; // For ICMP data with Echo request
86 ACE_Time_Value const ACE_Ping_Socket::time_default_ (0, 500000);
89 void
90 ACE_Ping_Socket::dump () const
92 ACE_TRACE ("ACE_Ping_Socket::dump");
95 ACE_Ping_Socket::ACE_Ping_Socket ()
97 ACE_TRACE ("ACE_Ping_Socket::ACE_Ping_Socket");
100 ACE_Ping_Socket::ACE_Ping_Socket (ACE_Addr const & local,
101 int protocol,
102 int reuse_addr)
103 : sequence_number_ (0),
104 connected_socket_ (false)
106 ACE_TRACE ("ACE_Ping_Socket::ACE_Ping_Socket");
108 ACE_OS::memset (icmp_send_buff_, 0, sizeof (icmp_send_buff_));
109 ACE_OS::memset (icmp_recv_buff_, 0, sizeof (icmp_recv_buff_));
111 if (this->open (local, protocol, reuse_addr) == -1)
113 ACELIB_DEBUG ((LM_DEBUG,
114 ACE_TEXT ("ACE_Ping_Socket::ACE_Ping_Socket: %p\n"),
115 ACE_TEXT ("open")));
116 return;
119 // trying to increase the size of socket receive buffer - some
120 // protection from multiple responses e.g., when falling to the
121 // multi-cast address
122 int size = 64 * 1024;
123 ACE_SOCK::set_option (SOL_SOCKET,
124 SO_RCVBUF,
125 (void *) &size,
126 sizeof (size));
129 ACE_Ping_Socket::~ACE_Ping_Socket ()
131 ACE_TRACE ("ACE_Ping_Socket::~ACE_Ping_Socket");
135 ACE_Ping_Socket::open (ACE_Addr const & local,
136 int protocol,
137 int reuse_addr)
139 ACE_TRACE ("ACE_Ping_Socket::open");
140 return inherited::open (local, protocol, reuse_addr);
144 ACE_Ping_Socket::receive_echo_reply (ACE_Time_Value const * timeout)
146 ACE_TRACE ("ACE_Ping_Socket::receive_echo_reply");
148 ACE_Time_Value before = ACE_OS::gettimeofday ();
149 ACE_Time_Value after;
150 ACE_Time_Value time_left;
151 ACE_Time_Value *wait_time = const_cast<ACE_Time_Value *> (timeout);
152 const ACE_Time_Value half_millisec (0, 500);
154 ACE_OS::memset (icmp_recv_buff_, 0, sizeof icmp_recv_buff_);
158 ssize_t rval_recv = inherited::recv (icmp_recv_buff_,
159 sizeof icmp_recv_buff_,
161 wait_time);
162 if (rval_recv < 0)
164 if (errno == EINTR)
166 after = ACE_OS::gettimeofday ();
167 time_left = *timeout - after + before;
169 // If more than .5 ms left, wait on select()
170 if (time_left > half_millisec)
172 wait_time = &time_left; // coming back to wait on select()
173 continue;
175 else
177 break;
180 return -1;
182 else if (!this->process_incoming_dgram (icmp_recv_buff_, rval_recv))
184 return 0; //= success
186 else
188 after = ACE_OS::gettimeofday ();
189 if ((after - before) >= *timeout)
191 errno = ETIMEDOUT;
192 break;
194 // new timeout, we are coming back to sit on select
195 *wait_time = *timeout - after + before;
197 } while (*wait_time >= half_millisec);
199 errno = ETIMEDOUT;
200 return -1;
204 ACE_Ping_Socket::process_incoming_dgram (char * ptr, ssize_t len)
206 unsigned char hlen1;
207 ssize_t icmplen;
208 struct ip * ip;
209 struct icmp * icmp;
211 ip = (struct ip *) ptr; // start of IP header
213 // Warning... using knowledge of IP header layout. This avoids a maze of
214 // #if blocks for various systems. The first byte of the header has the
215 // IP version in the left-most 4 bits and the length in the other 4 bits.
216 hlen1 = static_cast<unsigned char>(*ptr);
217 hlen1 <<= 4; // Bump the version off
218 hlen1 >>= 4; // Zero-extended length remains
219 hlen1 <<= 2; // Now it counts bytes, not words
221 icmp = (struct icmp *) (ptr + hlen1); // start of ICMP header
223 if ((icmplen = len - hlen1) < ICMP_MIN)
225 ACELIB_DEBUG
226 ((LM_DEBUG,
227 ACE_TEXT ("(%P|%t) ACE_Ping_Socket::process_incoming_dgram")
228 ACE_TEXT (" - ICMP length is %b < 8.\n"),
229 icmplen));
230 ACELIB_ERROR_RETURN
231 ((LM_ERROR,
232 ACE_TEXT ("(%P|%t) ACE_Ping_Socket::process_incoming_dgram - ")
233 ACE_TEXT ("The ICMP header either not received or is corrupted.")),
234 -1);
237 if (icmp->icmp_type == ICMP_ECHOREPLY)
239 ACELIB_DEBUG
240 ((LM_DEBUG,
241 ACE_TEXT ("(%P|%t) ACE_Ping_Socket::process_incoming_dgram")
242 ACE_TEXT (" - ICMP_ECHOREPLY received.\n")));
244 if (icmp->icmp_id != (ACE_OS::getpid () & 0xFFFF))
246 ACELIB_ERROR_RETURN
247 ((LM_ERROR,
248 ACE_TEXT ("(%P|%t) ACE_Ping_Socket::")
249 ACE_TEXT ("process_incoming_dgram ")
250 ACE_TEXT ("- The ICMP header received is a reply to request ")
251 ACE_TEXT ("of another process (%d; expected %d).\n"),
252 icmp->icmp_id, ACE_OS::getpid()),
253 -1);
255 if (icmplen < 16)
257 ACELIB_ERROR_RETURN
258 ((LM_ERROR,
259 ACE_TEXT ("(%P|%t) ACE_Ping_Socket::")
260 ACE_TEXT ("process_incoming_dgram - ICMP length ")
261 ACE_TEXT ("is %b < 16."),
262 icmplen),
263 -1);
266 ACELIB_DEBUG
267 ((LM_DEBUG,
268 ACE_TEXT ("(%P|%t) ACE::Ping_Socket::process_incoming_dgram - ")
269 ACE_TEXT ("received ")
270 ACE_TEXT ("ICMP datagram with length of %b bytes (not counting ")
271 ACE_TEXT ("IP-header): seq=%u, ttl=%d.\n"),
272 icmplen, icmp->icmp_seq, ip->ip_ttl));
274 return 0; //= success
277 ACELIB_DEBUG
278 ((LM_DEBUG,
279 ACE_TEXT ("(%P|%t) ACE::Ping_Socket::process_incoming_dgram - ")
280 ACE_TEXT ("received datagram that is not ICMP_ECHOREPLY.\n")));
282 return -1;
286 ACE_Ping_Socket::send_echo_check (ACE_INET_Addr &remote_addr,
287 bool to_connect)
289 if (this->get_handle () == ACE_INVALID_HANDLE)
291 errno = EBADF;
292 return -1;
295 sockaddr_in *addr_connect = 0;
296 addr_connect = (sockaddr_in *) remote_addr.get_addr ();
299 * Nulling port field to prevent strange behavior, when a raw
300 * socket is "connected" to a sockaddr_in with a non-nulled port.
302 ACE_OS::memset ((void*) &addr_connect->sin_port,
304 sizeof (addr_connect->sin_port));
306 // to connect the socket
307 if (to_connect && !this->connected_socket_)
309 if (ACE_OS::connect (this->get_handle (),
310 (sockaddr*) addr_connect,
311 remote_addr.get_size ()) == -1)
313 if (errno != EINTR)
314 return -1;
316 this->connected_socket_ = true;
319 ACE_OS::memset (this->icmp_send_buff_, 0, sizeof this->icmp_send_buff_);
320 int datalen = ICMP_DATA_LENGTH;
321 struct icmp *_icmp = 0;
323 _icmp = (struct icmp *) this->icmp_send_buff_;
324 _icmp->icmp_type = ICMP_ECHO;
325 _icmp->icmp_code = 0;
326 _icmp->icmp_id = ACE_OS::getpid () & 0xFFFF;
327 _icmp->icmp_seq = sequence_number_++;
329 #if defined (ACE_WIN32)
330 _icmp->icmp_data = GetTickCount ();
331 #else /* #if defined (ACE_WIN32) */
332 gettimeofday ((struct timeval *) &_icmp->icmp_data, 0);
333 #endif /* #if defined (ACE_WIN32) */
335 int length_icmp = ICMP_MIN + datalen; // checksum ICMP header and data.
336 _icmp->icmp_cksum = 0;
337 _icmp->icmp_cksum = inherited::calculate_checksum ((u_short *) _icmp,
338 length_icmp);
339 ssize_t rval_send = -1;
341 if ((rval_send = send ((void const *) icmp_send_buff_,
342 length_icmp,
343 remote_addr)) != length_icmp)
345 return -1;
347 return 0;
351 ACE_Ping_Socket::make_echo_check (ACE_INET_Addr & remote_addr,
352 bool to_connect,
353 ACE_Time_Value const * timeout)
355 int rval_send = -1;
357 if ((rval_send = this->send_echo_check (remote_addr,
358 to_connect)) == -1)
359 return -1;
361 ACELIB_DEBUG
362 ((LM_DEBUG,
363 ACE_TEXT ("(%P|%t) ACE_Ping_Socket::make_echo_check - sent %d.\n"),
364 rval_send));
366 return this->receive_echo_reply (timeout);
369 ACE_END_VERSIONED_NAMESPACE_DECL
371 #endif /* ACE_HAS_ICMP_SUPPORT == 1 */