PageRenderTime 24ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/dep/acelite/ace/Ping_Socket.cpp

https://code.google.com/p/oregoncore/
C++ | 370 lines | 282 code | 62 blank | 26 comment | 30 complexity | 8cb47f266977e3a456adf085fcdc2c42 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, BSD-3-Clause, GPL-2.0, LGPL-2.1
  1. // $Id: Ping_Socket.cpp 92069 2010-09-28 11:38:59Z johnnyw $
  2. #include "ace/Ping_Socket.h"
  3. #if defined (ACE_HAS_ICMP_SUPPORT) && (ACE_HAS_ICMP_SUPPORT == 1)
  4. #include "ace/INET_Addr.h"
  5. #include "ace/Log_Msg.h"
  6. #include "ace/OS_NS_string.h"
  7. #include "ace/OS_NS_sys_time.h"
  8. #include "ace/OS_NS_sys_socket.h"
  9. # include "ace/OS_NS_unistd.h"
  10. #if !defined (__ACE_INLINE__)
  11. # include "ace/Ping_Socket.inl"
  12. #endif /* !__ACE_INLINE__ */
  13. ACE_BEGIN_VERSIONED_NAMESPACE_DECL
  14. ACE_ALLOC_HOOK_DEFINE (ACE_Ping_Socket)
  15. ACE_END_VERSIONED_NAMESPACE_DECL
  16. //---------------------------------------------------------------------------
  17. // Better to arrange some os_include/netinet/ip.h and
  18. // os_include/netinet/icmp.h files ?
  19. //---------------------------------------------------------------------------
  20. #if !defined (ACE_WIN32)
  21. /*
  22. * This is where ICMP-related stuff is defined on any sane system...
  23. */
  24. #include /**/ <netinet/in_systm.h>
  25. #include /**/ <netinet/ip.h>
  26. #include /**/ <netinet/ip_icmp.h>
  27. #else /* #if ! defined (ACE_WIN32) */
  28. /*
  29. * This was a surpise to me... This stuff is not defined anywhere under MSVC.
  30. * These values have only been checked for NT4 and Win2K. They were taken from
  31. * the MSDN ping.c program and modified.
  32. */
  33. #define ICMP_ECHO 8
  34. #define ICMP_ECHOREPLY 0
  35. #pragma pack(1)
  36. struct ip
  37. {
  38. ACE_UINT8 ip_hl:4; // length of the header
  39. ACE_UINT8 version:4; // Version of IP
  40. ACE_UINT8 tos; // Type of service
  41. ACE_UINT16 total_len; // total length of the packet
  42. ACE_UINT16 ident; // unique identifier
  43. ACE_UINT16 frag_and_flags; // flags
  44. ACE_UINT8 ip_ttl; // Time to live
  45. ACE_UINT8 proto; // protocol (TCP, UDP etc)
  46. ACE_UINT16 checksum; // IP checksum
  47. ACE_UINT32 sourceIP;
  48. ACE_UINT32 destIP;
  49. };
  50. struct icmp
  51. {
  52. ACE_UINT8 icmp_type;
  53. ACE_UINT8 icmp_code; // type sub code
  54. ACE_UINT16 icmp_cksum;
  55. ACE_UINT16 icmp_id;
  56. ACE_UINT16 icmp_seq;
  57. ACE_UINT32 icmp_data; // time data
  58. };
  59. #pragma pack()
  60. #endif /* #if ! defined (ACE_WIN32) */
  61. ACE_BEGIN_VERSIONED_NAMESPACE_DECL
  62. int const ICMP_MIN = 8; // Minimal size of ICMP packet, header only
  63. int const ICMP_DATA_LENGTH = 56; // For ICMP data with Echo request
  64. ACE_Time_Value const ACE_Ping_Socket::time_default_ (0, 500000);
  65. void
  66. ACE_Ping_Socket::dump (void) const
  67. {
  68. ACE_TRACE ("ACE_Ping_Socket::dump");
  69. }
  70. ACE_Ping_Socket::ACE_Ping_Socket (void)
  71. {
  72. ACE_TRACE ("ACE_Ping_Socket::ACE_Ping_Socket");
  73. }
  74. ACE_Ping_Socket::ACE_Ping_Socket (ACE_Addr const & local,
  75. int protocol,
  76. int reuse_addr)
  77. : sequence_number_ (0),
  78. connected_socket_ (false)
  79. {
  80. ACE_TRACE ("ACE_Ping_Socket::ACE_Ping_Socket");
  81. ACE_OS::memset (icmp_send_buff_, 0, sizeof (icmp_send_buff_));
  82. ACE_OS::memset (icmp_recv_buff_, 0, sizeof (icmp_recv_buff_));
  83. if (this->open (local, protocol, reuse_addr) == -1)
  84. {
  85. ACE_DEBUG ((LM_DEBUG,
  86. ACE_TEXT ("ACE_Ping_Socket::ACE_Ping_Socket: %p\n"),
  87. ACE_TEXT ("open")));
  88. return;
  89. }
  90. // trying to increase the size of socket receive buffer - some
  91. // protection from multiple responces e.g., when falling to the
  92. // multi-cast address
  93. int size = 64 * 1024;
  94. ACE_SOCK::set_option (SOL_SOCKET,
  95. SO_RCVBUF,
  96. (void *) &size,
  97. sizeof (size));
  98. }
  99. ACE_Ping_Socket::~ACE_Ping_Socket (void)
  100. {
  101. ACE_TRACE ("ACE_Ping_Socket::~ACE_Ping_Socket");
  102. }
  103. int
  104. ACE_Ping_Socket::open (ACE_Addr const & local,
  105. int protocol,
  106. int reuse_addr)
  107. {
  108. ACE_TRACE ("ACE_Ping_Socket::open");
  109. return inherited::open (local, protocol, reuse_addr);
  110. }
  111. int
  112. ACE_Ping_Socket::receive_echo_reply (ACE_Time_Value const * timeout)
  113. {
  114. ACE_TRACE ("ACE_Ping_Socket::receive_echo_reply");
  115. ACE_Time_Value before = ACE_OS::gettimeofday ();
  116. ACE_Time_Value after;
  117. ACE_Time_Value time_left;
  118. ACE_Time_Value *wait_time = const_cast<ACE_Time_Value *> (timeout);
  119. const ACE_Time_Value half_millisec (0, 500);
  120. ACE_OS::memset (icmp_recv_buff_, 0, sizeof icmp_recv_buff_);
  121. do
  122. {
  123. int rval_recv = inherited::recv (icmp_recv_buff_,
  124. sizeof icmp_recv_buff_,
  125. 0,
  126. wait_time);
  127. if (rval_recv < 0)
  128. {
  129. if (errno == EINTR)
  130. {
  131. after = ACE_OS::gettimeofday ();
  132. time_left = *timeout - after + before;
  133. // If more than .5 ms left, wait on select()
  134. if (time_left > half_millisec)
  135. {
  136. wait_time = &time_left; // coming back to wait on select()
  137. continue;
  138. }
  139. else
  140. {
  141. break;
  142. }
  143. }
  144. return -1;
  145. }
  146. else if (!this->process_incoming_dgram (icmp_recv_buff_, rval_recv))
  147. {
  148. return 0; //= success
  149. }
  150. else
  151. {
  152. after = ACE_OS::gettimeofday ();
  153. if ((after - before) >= *timeout)
  154. {
  155. errno = ETIMEDOUT;
  156. break;
  157. }
  158. // new timeout, we are coming back to sit on select
  159. *wait_time = *timeout - after + before;
  160. }
  161. } while (*wait_time >= half_millisec);
  162. errno = ETIMEDOUT;
  163. return -1;
  164. }
  165. int
  166. ACE_Ping_Socket::process_incoming_dgram (char * ptr, ssize_t len)
  167. {
  168. unsigned char hlen1;
  169. int icmplen;
  170. struct ip * ip;
  171. struct icmp * icmp;
  172. ip = (struct ip *) ptr; // start of IP header
  173. // Warning... using knowledge of IP header layout. This avoids a maze of
  174. // #if blocks for various systems. The first byte of the header has the
  175. // IP version in the left-most 4 bits and the length in the other 4 bits.
  176. hlen1 = static_cast<unsigned char>(*ptr);
  177. hlen1 <<= 4; // Bump the version off
  178. hlen1 >>= 4; // Zero-extended length remains
  179. hlen1 <<= 2; // Now it counts bytes, not words
  180. icmp = (struct icmp *) (ptr + hlen1); // start of ICMP header
  181. if ((icmplen = len - hlen1) < ICMP_MIN)
  182. {
  183. ACE_DEBUG
  184. ((LM_DEBUG,
  185. ACE_TEXT ("(%P|%t) ACE_Ping_Socket::process_incoming_dgram")
  186. ACE_TEXT (" - ICMP length is %d < 8.\n"),
  187. icmplen));
  188. ACE_ERROR_RETURN
  189. ((LM_ERROR,
  190. ACE_TEXT ("(%P|%t) ACE_Ping_Socket::process_incoming_dgram - ")
  191. ACE_TEXT ("The ICMP header either not received or is corrupted.")),
  192. -1);
  193. }
  194. if (icmp->icmp_type == ICMP_ECHOREPLY)
  195. {
  196. ACE_DEBUG
  197. ((LM_DEBUG,
  198. ACE_TEXT ("(%P|%t) ACE_Ping_Socket::process_incoming_dgram")
  199. ACE_TEXT (" - ICMP_ECHOREPLY received.\n")));
  200. if (icmp->icmp_id != (ACE_OS::getpid () & 0xFFFF))
  201. {
  202. ACE_ERROR_RETURN
  203. ((LM_ERROR,
  204. ACE_TEXT ("(%P|%t) ACE_Ping_Socket::")
  205. ACE_TEXT ("process_incoming_dgram ")
  206. ACE_TEXT ("- The ICMP header received is a reply to request ")
  207. ACE_TEXT ("of another process (%d; expected %d).\n"),
  208. icmp->icmp_id, ACE_OS::getpid()),
  209. -1);
  210. }
  211. if (icmplen < 16)
  212. {
  213. ACE_ERROR_RETURN
  214. ((LM_ERROR,
  215. ACE_TEXT ("(%P|%t) ACE_Ping_Socket::")
  216. ACE_TEXT ("process_incoming_dgram - ICMP length ")
  217. ACE_TEXT ("is %d < 16."),
  218. icmplen),
  219. -1);
  220. }
  221. ACE_DEBUG
  222. ((LM_DEBUG,
  223. ACE_TEXT ("(%P|%t) ACE::Ping_Socket::process_incoming_dgram - ")
  224. ACE_TEXT ("received ")
  225. ACE_TEXT ("ICMP datagram with length of %d bytes (not counting ")
  226. ACE_TEXT ("IP-header): seq=%u, ttl=%d.\n"),
  227. icmplen, icmp->icmp_seq, ip->ip_ttl));
  228. return 0; //= success
  229. }
  230. ACE_DEBUG
  231. ((LM_DEBUG,
  232. ACE_TEXT ("(%P|%t) ACE::Ping_Socket::process_incoming_dgram - ")
  233. ACE_TEXT ("received datagram that is not ICMP_ECHOREPLY.\n")));
  234. return -1;
  235. }
  236. int
  237. ACE_Ping_Socket::send_echo_check (ACE_INET_Addr &remote_addr,
  238. bool to_connect)
  239. {
  240. if (this->get_handle () == ACE_INVALID_HANDLE)
  241. {
  242. errno = EBADF;
  243. return -1;
  244. }
  245. sockaddr_in *addr_connect = 0;
  246. addr_connect = (sockaddr_in *) remote_addr.get_addr ();
  247. /*
  248. * Nulling port field to prevent strange behavior, when a raw
  249. * socket is "connected" to a sockaddr_in with a non-nulled port.
  250. */
  251. ACE_OS::memset ((void*) &addr_connect->sin_port,
  252. 0,
  253. sizeof (addr_connect->sin_port));
  254. // to connect the socket
  255. if (to_connect && !this->connected_socket_)
  256. {
  257. if (ACE_OS::connect (this->get_handle (),
  258. (sockaddr*) addr_connect,
  259. remote_addr.get_size ()) == -1)
  260. {
  261. if (errno != EINTR)
  262. return -1;
  263. }
  264. this->connected_socket_ = true;
  265. }
  266. ACE_OS::memset (this->icmp_send_buff_, 0, sizeof this->icmp_send_buff_);
  267. int datalen = ICMP_DATA_LENGTH;
  268. struct icmp *_icmp = 0;
  269. _icmp = (struct icmp *) this->icmp_send_buff_;
  270. _icmp->icmp_type = ICMP_ECHO;
  271. _icmp->icmp_code = 0;
  272. _icmp->icmp_id = ACE_OS::getpid () & 0xFFFF;
  273. _icmp->icmp_seq = sequence_number_++;
  274. #if defined (ACE_WIN32)
  275. _icmp->icmp_data = GetTickCount ();
  276. #else /* #if defined (ACE_WIN32) */
  277. gettimeofday ((struct timeval *) &_icmp->icmp_data, 0);
  278. #endif /* #if defined (ACE_WIN32) */
  279. int length_icmp = ICMP_MIN + datalen; // checksum ICMP header and data.
  280. _icmp->icmp_cksum = 0;
  281. _icmp->icmp_cksum = inherited::calculate_checksum ((u_short *) _icmp,
  282. length_icmp);
  283. int rval_send = -1;
  284. if ((rval_send = send ((void const *) icmp_send_buff_,
  285. length_icmp,
  286. remote_addr)) != length_icmp)
  287. {
  288. return -1;
  289. }
  290. return 0;
  291. }
  292. int
  293. ACE_Ping_Socket::make_echo_check (ACE_INET_Addr & remote_addr,
  294. bool to_connect,
  295. ACE_Time_Value const * timeout)
  296. {
  297. int rval_send = -1;
  298. if ((rval_send = this->send_echo_check (remote_addr,
  299. to_connect)) == -1)
  300. return -1;
  301. ACE_DEBUG
  302. ((LM_DEBUG,
  303. ACE_TEXT ("(%P|%t) ACE_Ping_Socket::make_echo_check - sent %d.\n"),
  304. rval_send));
  305. return this->receive_echo_reply (timeout);
  306. }
  307. ACE_END_VERSIONED_NAMESPACE_DECL
  308. #endif /* ACE_HAS_ICMP_SUPPORT == 1 */