2014-06-11 19:17:28 +00:00
|
|
|
#include <fc/network/ntp.hpp>
|
|
|
|
|
#include <fc/network/udp_socket.hpp>
|
|
|
|
|
#include <fc/network/resolve.hpp>
|
|
|
|
|
#include <fc/network/ip.hpp>
|
2014-06-16 15:17:29 +00:00
|
|
|
#include <fc/thread/thread.hpp>
|
2014-06-11 19:17:28 +00:00
|
|
|
|
2014-06-11 23:14:04 +00:00
|
|
|
#include <stdint.h>
|
|
|
|
|
#include "../byteswap.hpp"
|
2014-06-11 20:32:47 +00:00
|
|
|
|
2014-07-26 22:22:38 +00:00
|
|
|
#include <atomic>
|
2014-06-11 19:17:28 +00:00
|
|
|
#include <array>
|
|
|
|
|
|
|
|
|
|
namespace fc
|
|
|
|
|
{
|
2014-06-16 15:17:29 +00:00
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
|
|
class ntp_impl
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
/** vector < host, port > */
|
2014-09-08 19:31:13 +00:00
|
|
|
fc::thread _ntp_thread;
|
2014-06-16 15:17:29 +00:00
|
|
|
std::vector< std::pair< std::string, uint16_t> > _ntp_hosts;
|
2014-07-26 22:22:38 +00:00
|
|
|
fc::future<void> _read_loop_done;
|
2014-06-16 15:17:29 +00:00
|
|
|
udp_socket _sock;
|
|
|
|
|
uint32_t _request_interval_sec;
|
2014-06-26 03:32:09 +00:00
|
|
|
fc::time_point _last_request_time;
|
2014-07-26 22:22:38 +00:00
|
|
|
|
|
|
|
|
std::atomic_bool _last_ntp_delta_initialized;
|
|
|
|
|
std::atomic<int64_t> _last_ntp_delta_microseconds;
|
|
|
|
|
|
2014-06-16 15:17:29 +00:00
|
|
|
|
2014-07-26 22:22:38 +00:00
|
|
|
fc::future<void> _request_time_task_done;
|
|
|
|
|
|
|
|
|
|
ntp_impl() :
|
2014-09-08 19:31:13 +00:00
|
|
|
_ntp_thread("ntp"),
|
2014-07-26 22:22:38 +00:00
|
|
|
_request_interval_sec( 60*60 /* 1 hr */),
|
2014-09-09 15:08:27 +00:00
|
|
|
_last_ntp_delta_microseconds(0)
|
2014-07-26 22:22:38 +00:00
|
|
|
{
|
|
|
|
|
_last_ntp_delta_initialized = false;
|
|
|
|
|
_ntp_hosts.push_back( std::make_pair( "pool.ntp.org",123 ) );
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 19:31:13 +00:00
|
|
|
~ntp_impl()
|
|
|
|
|
{
|
|
|
|
|
_ntp_thread.quit(); //TODO: this can be removed once fc::threads call quit during destruction
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-16 15:17:29 +00:00
|
|
|
void request_now()
|
|
|
|
|
{
|
2014-07-26 22:22:38 +00:00
|
|
|
assert(_ntp_thread.is_current());
|
2014-06-16 15:17:29 +00:00
|
|
|
for( auto item : _ntp_hosts )
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2014-09-29 22:55:35 +00:00
|
|
|
wlog( "resolving... ${r}", ("r", item) );
|
2014-06-16 15:17:29 +00:00
|
|
|
auto eps = resolve( item.first, item.second );
|
|
|
|
|
for( auto ep : eps )
|
|
|
|
|
{
|
|
|
|
|
ilog( "sending request to ${ep}", ("ep",ep) );
|
2014-09-12 23:42:25 +00:00
|
|
|
std::shared_ptr<char> send_buffer(new char[48], [](char* p){ delete[] p; });
|
|
|
|
|
std::array<unsigned char, 48> packet_to_send { {010,0,0,0,0,0,0,0,0} };
|
|
|
|
|
memcpy(send_buffer.get(), packet_to_send.data(), packet_to_send.size());
|
2014-06-26 03:32:09 +00:00
|
|
|
_last_request_time = fc::time_point::now();
|
2014-09-12 23:42:25 +00:00
|
|
|
_sock.send_to( send_buffer, packet_to_send.size(), ep );
|
2014-06-16 18:04:36 +00:00
|
|
|
break;
|
2014-06-16 15:17:29 +00:00
|
|
|
}
|
|
|
|
|
}
|
2014-08-25 22:43:12 +00:00
|
|
|
catch (const fc::canceled_exception&)
|
|
|
|
|
{
|
|
|
|
|
throw;
|
|
|
|
|
}
|
2014-06-16 15:17:29 +00:00
|
|
|
// this could fail to resolve but we want to go on to other hosts..
|
|
|
|
|
catch ( const fc::exception& e )
|
|
|
|
|
{
|
2014-07-26 22:22:38 +00:00
|
|
|
elog( "${e}", ("e",e.to_detail_string() ) );
|
2014-06-16 15:17:29 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} // request_now
|
|
|
|
|
|
2014-08-01 15:31:36 +00:00
|
|
|
//started for first time in ntp() constructor, canceled in ~ntp() destructor
|
2014-07-26 22:22:38 +00:00
|
|
|
void request_time_task()
|
2014-06-16 15:17:29 +00:00
|
|
|
{
|
2014-08-01 15:31:36 +00:00
|
|
|
assert(_ntp_thread.is_current());
|
2014-06-26 03:32:09 +00:00
|
|
|
request_now();
|
2014-08-12 19:19:44 +00:00
|
|
|
if (!_request_time_task_done.canceled())
|
|
|
|
|
_request_time_task_done = schedule( [=](){ request_time_task(); },
|
|
|
|
|
fc::time_point::now() + fc::seconds(_request_interval_sec),
|
|
|
|
|
"request_time_task" );
|
2014-06-16 15:17:29 +00:00
|
|
|
} // request_loop
|
|
|
|
|
|
2014-09-08 19:31:13 +00:00
|
|
|
void start_read_loop()
|
|
|
|
|
{
|
|
|
|
|
_read_loop_done = _ntp_thread.async( [this](){ read_loop(); }, "ntp_read_loop" );
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-16 15:17:29 +00:00
|
|
|
void read_loop()
|
|
|
|
|
{
|
2014-07-26 22:22:38 +00:00
|
|
|
assert(_ntp_thread.is_current());
|
2014-09-08 19:31:13 +00:00
|
|
|
|
2014-09-09 15:10:37 +00:00
|
|
|
uint32_t receive_buffer_size = sizeof(uint64_t) * 1024;
|
|
|
|
|
std::shared_ptr<char> receive_buffer(new char[receive_buffer_size], [](char* p){ delete[] p; });
|
|
|
|
|
uint64_t* recv_buf = (uint64_t*)receive_buffer.get();
|
|
|
|
|
|
2014-09-08 19:31:13 +00:00
|
|
|
//outer while to restart read-loop if exception is thrown while waiting to receive on socket.
|
2014-09-14 23:53:08 +00:00
|
|
|
while( !_read_loop_done.canceled() )
|
2014-06-16 15:17:29 +00:00
|
|
|
{
|
2014-09-08 19:31:13 +00:00
|
|
|
// if you start the read while loop here, the recieve_from call will throw "invalid argument" on win32,
|
|
|
|
|
// so instead we start the loop after making our first request
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
_sock.open();
|
|
|
|
|
request_time_task();
|
|
|
|
|
|
|
|
|
|
while( !_read_loop_done.canceled() )
|
|
|
|
|
{
|
|
|
|
|
fc::ip::endpoint from;
|
|
|
|
|
try
|
|
|
|
|
{
|
2014-09-09 15:10:37 +00:00
|
|
|
_sock.receive_from( receive_buffer, receive_buffer_size, from );
|
2014-09-08 19:31:13 +00:00
|
|
|
} FC_RETHROW_EXCEPTIONS(error, "Error reading from NTP socket");
|
|
|
|
|
|
|
|
|
|
uint64_t receive_timestamp_net_order = recv_buf[4];
|
|
|
|
|
uint64_t receive_timestamp_host = bswap_64(receive_timestamp_net_order);
|
|
|
|
|
uint32_t fractional_seconds = receive_timestamp_host & 0xffffffff;
|
|
|
|
|
uint32_t microseconds = (uint32_t)(((((uint64_t)fractional_seconds) * 1000000) + (UINT64_C(1)<<31)) >> 32);
|
|
|
|
|
uint32_t seconds_since_1900 = receive_timestamp_host >> 32;
|
|
|
|
|
uint32_t seconds_since_epoch = seconds_since_1900 - 2208988800;
|
|
|
|
|
|
|
|
|
|
if( fc::time_point::now() - _last_request_time > fc::seconds(1) )
|
|
|
|
|
request_now();
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
auto ntp_time = (fc::time_point() + fc::seconds(seconds_since_epoch) + fc::microseconds(microseconds));
|
|
|
|
|
if( ntp_time - fc::time_point::now() < fc::seconds(60*60*24) &&
|
|
|
|
|
fc::time_point::now() - ntp_time < fc::seconds(60*60*24) )
|
|
|
|
|
{
|
|
|
|
|
_last_ntp_delta_microseconds = (ntp_time - fc::time_point::now()).count();
|
|
|
|
|
_last_ntp_delta_initialized = true;
|
2014-09-29 22:55:35 +00:00
|
|
|
fc::microseconds ntp_delta_time = fc::microseconds(_last_ntp_delta_microseconds);
|
|
|
|
|
wlog("ntp_delta_time updated to ${delta_time}", ("delta_time",ntp_delta_time) );
|
2014-09-08 19:31:13 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
elog( "NTP time is way off ${time}", ("time",ntp_time)("local",fc::time_point::now()) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} // try
|
|
|
|
|
catch (fc::canceled_exception)
|
|
|
|
|
{
|
2014-09-14 23:53:08 +00:00
|
|
|
throw;
|
2014-09-08 19:31:13 +00:00
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
//swallow any other exception and restart loop
|
|
|
|
|
elog("unexpected exception in read_loop, going to restart it.");
|
|
|
|
|
}
|
|
|
|
|
_sock.close();
|
2014-09-09 15:08:27 +00:00
|
|
|
fc::usleep(fc::seconds(_request_interval_sec));
|
2014-09-08 19:31:13 +00:00
|
|
|
} //outer while loop
|
2014-09-29 22:55:35 +00:00
|
|
|
wlog("exiting ntp read_loop");
|
2014-09-08 19:31:13 +00:00
|
|
|
} //end read_loop()
|
2014-09-29 22:55:35 +00:00
|
|
|
}; //ntp_impl
|
2014-06-16 15:17:29 +00:00
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
2014-06-11 19:17:28 +00:00
|
|
|
|
|
|
|
|
|
2014-06-16 15:17:29 +00:00
|
|
|
ntp::ntp()
|
|
|
|
|
:my( new detail::ntp_impl() )
|
2014-06-11 19:17:28 +00:00
|
|
|
{
|
2014-09-08 19:31:13 +00:00
|
|
|
my->start_read_loop();
|
2014-06-16 15:17:29 +00:00
|
|
|
}
|
2014-06-11 19:17:28 +00:00
|
|
|
|
2014-06-16 15:17:29 +00:00
|
|
|
ntp::~ntp()
|
|
|
|
|
{
|
2014-07-26 22:22:38 +00:00
|
|
|
my->_ntp_thread.async([=](){
|
|
|
|
|
try
|
|
|
|
|
{
|
2014-08-27 18:07:44 +00:00
|
|
|
my->_request_time_task_done.cancel_and_wait("ntp object is destructing");
|
2014-07-26 22:22:38 +00:00
|
|
|
}
|
|
|
|
|
catch ( const fc::exception& e )
|
|
|
|
|
{
|
|
|
|
|
wlog( "Exception thrown while shutting down NTP's request_time_task, ignoring: ${e}", ("e",e) );
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
wlog( "Exception thrown while shutting down NTP's request_time_task, ignoring" );
|
|
|
|
|
}
|
2014-09-09 15:08:27 +00:00
|
|
|
|
2014-07-26 22:22:38 +00:00
|
|
|
try
|
|
|
|
|
{
|
2014-09-08 19:31:13 +00:00
|
|
|
my->_read_loop_done.cancel_and_wait("ntp object is destructing");
|
2014-07-26 22:22:38 +00:00
|
|
|
}
|
|
|
|
|
catch ( const fc::exception& e )
|
|
|
|
|
{
|
|
|
|
|
wlog( "Exception thrown while shutting down NTP's read_loop, ignoring: ${e}", ("e",e) );
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
wlog( "Exception thrown while shutting down NTP's read_loop, ignoring" );
|
|
|
|
|
}
|
2014-09-09 15:08:27 +00:00
|
|
|
|
2014-07-27 04:08:35 +00:00
|
|
|
}, "ntp_shutdown_task").wait();
|
2014-06-16 15:17:29 +00:00
|
|
|
}
|
2014-06-11 19:17:28 +00:00
|
|
|
|
|
|
|
|
|
2014-06-16 15:17:29 +00:00
|
|
|
void ntp::add_server( const std::string& hostname, uint16_t port)
|
|
|
|
|
{
|
2014-07-26 22:22:38 +00:00
|
|
|
my->_ntp_thread.async( [=](){ my->_ntp_hosts.push_back( std::make_pair(hostname,port) ); }, "add_server" ).wait();
|
2014-06-16 15:17:29 +00:00
|
|
|
}
|
2014-06-11 19:17:28 +00:00
|
|
|
|
2014-06-16 15:17:29 +00:00
|
|
|
void ntp::set_request_interval( uint32_t interval_sec )
|
|
|
|
|
{
|
|
|
|
|
my->_request_interval_sec = interval_sec;
|
|
|
|
|
}
|
2014-06-26 03:32:09 +00:00
|
|
|
|
2014-06-16 15:17:29 +00:00
|
|
|
void ntp::request_now()
|
|
|
|
|
{
|
2014-07-26 22:22:38 +00:00
|
|
|
my->_ntp_thread.async( [=](){ my->request_now(); }, "request_now" ).wait();
|
2014-06-16 15:17:29 +00:00
|
|
|
}
|
2014-06-11 23:14:04 +00:00
|
|
|
|
2014-06-16 15:17:29 +00:00
|
|
|
optional<time_point> ntp::get_time()const
|
|
|
|
|
{
|
2014-07-26 22:22:38 +00:00
|
|
|
if( my->_last_ntp_delta_initialized )
|
|
|
|
|
return fc::time_point::now() + fc::microseconds(my->_last_ntp_delta_microseconds);
|
2014-06-16 15:17:29 +00:00
|
|
|
return optional<time_point>();
|
2014-06-11 19:17:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|