diff --git a/HTTPServer.cpp b/HTTPServer.cpp index 2e9a02ca..487e9c83 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -180,6 +180,18 @@ namespace http { case eRouterStatusOK: s << "OK"; break; case eRouterStatusTesting: s << "Testing"; break; case eRouterStatusFirewalled: s << "Firewalled"; break; + case eRouterStatusError: + { + s << "Error"; + switch (i2p::context.GetError ()) + { + case eRouterErrorClockSkew: + s << "
Clock skew"; + break; + default: ; + } + break; + } default: s << "Unknown"; } s << "
\r\n"; @@ -344,50 +356,32 @@ namespace http { void ShowLeasesSets(std::stringstream& s) { - s << "
LeaseSets:

"; + s << "
LeaseSets (click on to show info):

\r\n"; + int counter = 1; // for each lease set i2p::data::netdb.VisitLeaseSets( - [&s](const i2p::data::IdentHash dest, std::shared_ptr leaseSet) + [&s, &counter](const i2p::data::IdentHash dest, std::shared_ptr leaseSet) { // create copy of lease set so we extract leases i2p::data::LeaseSet ls(leaseSet->GetBuffer(), leaseSet->GetBufferLen()); - // begin lease set entry s << "
"; - // invalid ? + s << "'>\r\n"; if (!ls.IsValid()) - s << "
!! Invalid !!
"; - // ident - s << "
" << dest.ToBase32() << "
"; - // LeaseSet time - s << "
expires: " << ls.GetExpirationTime() << "
"; - // get non expired leases + s << "
!! Invalid !!
\r\n"; + s << "
\r\n"; + s << "\r\n

\r\n"; + s << "Expires: " << ls.GetExpirationTime() << "
\r\n"; auto leases = ls.GetNonExpiredLeases(); - // show non expired leases - s << "

Non Expired Leases: " << leases.size() << "
"; - // for each lease - s << "
"; + s << "Non Expired Leases: " << leases.size() << "
\r\n"; for ( auto & l : leases ) { - // begin lease - s << "
"; - // gateway - s << "
Gateway: " << l->tunnelGateway.ToBase64() << "
"; - // tunnel id - s << "
TunnelID: " << l->tunnelID << "
"; - // end date - s << "
EndDate: " << l->endDate << "
"; - // end lease - s << "
"; + s << "Gateway: " << l->tunnelGateway.ToBase64() << "
\r\n"; + s << "TunnelID: " << l->tunnelID << "
\r\n"; + s << "EndDate: " << l->endDate << "
\r\n"; } - // end for each lease - s << "
"; - // end lease set entry - s << "
"; - // linebreak - s << "
"; + s << "

\r\n
\r\n\r\n"; } ); // end for each lease set diff --git a/I2CP.cpp b/I2CP.cpp index 0352cd15..41f81155 100644 --- a/I2CP.cpp +++ b/I2CP.cpp @@ -109,7 +109,7 @@ namespace client } else { - auto outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel (); + outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel (); auto leases = remote->GetNonExpiredLeases (); if (!leases.empty ()) remoteLease = leases[rand () % leases.size ()]; diff --git a/RouterContext.cpp b/RouterContext.cpp index c92b575b..6824adb8 100644 --- a/RouterContext.cpp +++ b/RouterContext.cpp @@ -18,7 +18,7 @@ namespace i2p RouterContext::RouterContext (): m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false), - m_StartupTime (0), m_Status (eRouterStatusOK ) + m_StartupTime (0), m_Status (eRouterStatusOK), m_Error (eRouterErrorNone) { } @@ -95,6 +95,7 @@ namespace i2p if (status != m_Status) { m_Status = status; + m_Error = eRouterErrorNone; switch (m_Status) { case eRouterStatusOK: diff --git a/RouterContext.h b/RouterContext.h index 27e0947d..b89b3140 100644 --- a/RouterContext.h +++ b/RouterContext.h @@ -20,9 +20,16 @@ namespace i2p { eRouterStatusOK = 0, eRouterStatusTesting = 1, - eRouterStatusFirewalled = 2 + eRouterStatusFirewalled = 2, + eRouterStatusError = 3 }; + enum RouterError + { + eRouterErrorNone = 0, + eRouterErrorClockSkew = 1 + }; + class RouterContext: public i2p::garlic::GarlicDestination { public: @@ -49,7 +56,9 @@ namespace i2p uint64_t GetBandwidthLimit () const { return m_BandwidthLimit; }; RouterStatus GetStatus () const { return m_Status; }; void SetStatus (RouterStatus status); - + RouterError GetError () const { return m_Error; }; + void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; }; + void UpdatePort (int port); // called from Daemon void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer); @@ -107,6 +116,7 @@ namespace i2p uint64_t m_StartupTime; // in seconds since epoch uint32_t m_BandwidthLimit; // allowed bandwidth RouterStatus m_Status; + RouterError m_Error; std::mutex m_GarlicMutex; }; diff --git a/RouterInfo.cpp b/RouterInfo.cpp index 9b2a4195..ff08286a 100644 --- a/RouterInfo.cpp +++ b/RouterInfo.cpp @@ -3,6 +3,10 @@ #include "I2PEndian.h" #include #include +#include +#if (BOOST_VERSION >= 105300) +#include +#endif #include "version.h" #include "Crypto.h" #include "Base.h" @@ -17,14 +21,14 @@ namespace data { RouterInfo::RouterInfo (): m_Buffer (nullptr) { - m_Addresses = std::make_shared(); // create empty list + m_Addresses = boost::make_shared(); // create empty list } RouterInfo::RouterInfo (const std::string& fullPath): m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false), m_SupportedTransports (0), m_Caps (0) { - m_Addresses = std::make_shared(); // create empty list + m_Addresses = boost::make_shared(); // create empty list m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; ReadFromFile (); } @@ -32,7 +36,7 @@ namespace data RouterInfo::RouterInfo (const uint8_t * buf, int len): m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), m_Caps (0) { - m_Addresses = std::make_shared(); // create empty list + m_Addresses = boost::make_shared(); // create empty list m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; memcpy (m_Buffer, buf, len); m_BufferLen = len; @@ -154,7 +158,7 @@ namespace data s.read ((char *)&m_Timestamp, sizeof (m_Timestamp)); m_Timestamp = be64toh (m_Timestamp); // read addresses - auto addresses = std::make_shared(); + auto addresses = boost::make_shared(); uint8_t numAddresses; s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return; bool introducers = false; @@ -255,7 +259,11 @@ namespace data m_SupportedTransports |= supportedTransports; } } - m_Addresses = addresses; +#if (BOOST_VERSION >= 105300) + boost::atomic_store (&m_Addresses, addresses); +#else + m_Addresses = addresses; // race condition +#endif // read peers uint8_t numPeers; s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return; diff --git a/RouterInfo.h b/RouterInfo.h index f4c077ef..e8a4ea75 100644 --- a/RouterInfo.h +++ b/RouterInfo.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "Identity.h" #include "Profiling.h" @@ -201,7 +202,7 @@ namespace data uint8_t * m_Buffer; size_t m_BufferLen; uint64_t m_Timestamp; - std::shared_ptr m_Addresses; + boost::shared_ptr m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9 std::map m_Properties; bool m_IsUpdated, m_IsUnreachable; uint8_t m_SupportedTransports, m_Caps; diff --git a/SSUSession.cpp b/SSUSession.cpp index 26c14570..5cd59164 100644 --- a/SSUSession.cpp +++ b/SSUSession.cpp @@ -272,6 +272,16 @@ namespace transport s.Insert (payload, 8); // relayTag and signed on time m_RelayTag = bufbe32toh (payload); payload += 4; // relayTag + if (i2p::context.GetStatus () == eRouterStatusTesting) + { + auto ts = i2p::util::GetSecondsSinceEpoch (); + uint32_t signedOnTime = bufbe32toh(payload); + if (signedOnTime < ts - SSU_CLOCK_SKEW || signedOnTime > ts + SSU_CLOCK_SKEW) + { + LogPrint (eLogError, "SSU: clock skew detected ", (int)ts - signedOnTime, ". Check your clock"); + i2p::context.SetError (eRouterErrorClockSkew); + } + } payload += 4; // signed on time // decrypt signature size_t signatureLen = m_RemoteIdentity->GetSignatureLen (); @@ -310,6 +320,14 @@ namespace transport SetRemoteIdentity (std::make_shared (payload, identitySize)); m_Data.UpdatePacketSize (m_RemoteIdentity->GetIdentHash ()); payload += identitySize; // identity + auto ts = i2p::util::GetSecondsSinceEpoch (); + uint32_t signedOnTime = bufbe32toh(payload); + if (signedOnTime < ts - SSU_CLOCK_SKEW || signedOnTime > ts + SSU_CLOCK_SKEW) + { + LogPrint (eLogError, "SSU message 'confirmed' time difference ", (int)ts - signedOnTime, " exceeds clock skew"); + Failed (); + return; + } if (m_SignedData) m_SignedData->Insert (payload, 4); // insert Alice's signed on time payload += 4; // signed-on time diff --git a/SSUSession.h b/SSUSession.h index 69669187..4838be2a 100644 --- a/SSUSession.h +++ b/SSUSession.h @@ -27,6 +27,7 @@ namespace transport const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes + const int SSU_CLOCK_SKEW = 60; // in seconds // payload types (4 bits) const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0; diff --git a/Timestamp.cpp b/Timestamp.cpp new file mode 100644 index 00000000..fbe51ea1 --- /dev/null +++ b/Timestamp.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include "I2PEndian.h" +#include "Timestamp.h" + +namespace i2p +{ +namespace util +{ + std::chrono::system_clock::duration g_TimeOffset = std::chrono::system_clock::duration::zero (); + + void SyncTimeWithNTP (const std::string& address) + { + boost::asio::io_service service; + boost::asio::ip::udp::resolver::query query (boost::asio::ip::udp::v4 (), address, "ntp"); + boost::system::error_code ec; + auto it = boost::asio::ip::udp::resolver (service).resolve (query, ec); + if (!ec && it != boost::asio::ip::udp::resolver::iterator()) + { + auto ep = (*it).endpoint (); // take first one + boost::asio::ip::udp::socket socket (service); + socket.open (boost::asio::ip::udp::v4 (), ec); + if (!ec) + { + uint8_t request[48];// 48 bytes NTP request + memset (request, 0, 48); + request[0] = 0x80; // client mode, version 0 + uint8_t * response = new uint8_t[1500]; // MTU + size_t len = 0; + try + { + socket.send_to (boost::asio::buffer (request, 48), ep); + len = socket.receive_from (boost::asio::buffer (response, 1500), ep); + } + catch (std::exception& e) + { + } + if (len >= 8) + { + uint32_t ts = bufbe32toh (response + 4); + if (ts > 2208988800U) ts -= 2208988800U; // 1/1/1970 from 1/1/1900 + } + delete[] response; + } + } + } +} +} + diff --git a/Timestamp.h b/Timestamp.h index d48cb164..2e61d856 100644 --- a/Timestamp.h +++ b/Timestamp.h @@ -8,6 +8,8 @@ namespace i2p { namespace util { + extern std::chrono::system_clock::duration g_TimeOffset; + inline uint64_t GetMillisecondsSinceEpoch () { return std::chrono::duration_cast( diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 90a679b2..a31fcfb2 100755 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -57,6 +57,7 @@ LOCAL_SRC_FILES := DaemonAndroid.cpp i2pd_android.cpp \ ../../TunnelEndpoint.cpp \ ../../TunnelGateway.cpp \ ../../TunnelPool.cpp \ + ../../Timestamp.cpp \ ../../util.cpp \ ../../i2pd.cpp ../../UPnP.cpp diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 61b8fdf9..95f223ef 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -55,6 +55,7 @@ set (LIBI2PD_SRC "${CMAKE_SOURCE_DIR}/Datagram.cpp" "${CMAKE_SOURCE_DIR}/Family.cpp" "${CMAKE_SOURCE_DIR}/Signature.cpp" + "${CMAKE_SOURCE_DIR}/Timestamp.cpp" "${CMAKE_SOURCE_DIR}/api.cpp" ) diff --git a/docs/build_notes_windows.md b/docs/build_notes_windows.md index 827d0123..a217d80d 100644 --- a/docs/build_notes_windows.md +++ b/docs/build_notes_windows.md @@ -27,7 +27,7 @@ msys2 ### x86 (32-bit architecture) -Get install file msys2-i686-20150916.exe from https://msys2.github.io. +Get install file msys2-i686-*.exe from https://msys2.github.io. open MSYS2 Shell (from Start menu). Install all prerequisites and download i2pd source: @@ -44,7 +44,7 @@ make ### x64 (64-bit architecture) -Get install file msys2-x86_64-20150916.exe from https://msys2.github.io. +Get install file msys2-x86_64-*.exe from https://msys2.github.io. open MSYS2 Shell (from Start menu). Install all prerequisites and download i2pd source: @@ -97,9 +97,9 @@ Requirements for building: * [CMake](https://cmake.org/) (tested with 3.1.3) * [Visual Studio Community Edition](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx) (tested with VS2013 Update 4) * [Boost](http://www.boost.org/) (tested with 1.59) -* Optionally [MiniUPnP](http://miniupnp.free.f) (tested with 1.9), we need only few client headers +* Optionally [MiniUPnP](http://miniupnp.free.fr) (tested with 1.9), we need only few client headers * OpenSSL (tested with 1.0.1p and 1.0.2e), if building from sources (recommended), you'll need as well - * [Netwide assembler](www.nasm.us) + * [Netwide assembler](http://www.nasm.us/) * Strawberry Perl or ActiveState Perl, do NOT try msys2 perl, it won't work diff --git a/filelist.mk b/filelist.mk index db243866..cb1263e3 100644 --- a/filelist.mk +++ b/filelist.mk @@ -5,7 +5,7 @@ LIB_SRC = \ SSUSession.cpp SSUData.cpp Streaming.cpp Identity.cpp TransitTunnel.cpp \ Transports.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelPool.cpp TunnelGateway.cpp \ Destination.cpp Base.cpp I2PEndian.cpp FS.cpp Config.cpp Family.cpp \ - Config.cpp HTTP.cpp util.cpp api.cpp + Config.cpp HTTP.cpp Timestamp.cpp util.cpp api.cpp LIB_CLIENT_SRC = \ AddressBook.cpp BOB.cpp ClientContext.cpp I2PTunnel.cpp I2PService.cpp \ diff --git a/qt/i2pd_qt/i2pd_qt.pro b/qt/i2pd_qt/i2pd_qt.pro index 90ab6c10..0972f65c 100644 --- a/qt/i2pd_qt/i2pd_qt.pro +++ b/qt/i2pd_qt/i2pd_qt.pro @@ -35,7 +35,8 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \ ../../RouterInfo.cpp ../../SAM.cpp ../../Signature.cpp ../../SOCKS.cpp ../../SSU.cpp \ ../../SSUData.cpp ../../SSUSession.cpp ../../Streaming.cpp ../../TransitTunnel.cpp \ ../../Transports.cpp ../../Tunnel.cpp ../../TunnelEndpoint.cpp ../../TunnelGateway.cpp \ - ../../TunnelPool.cpp ../../UPnP.cpp ../../util.cpp ../../Gzip.cpp ../../i2pd.cpp + ../../TunnelPool.cpp ../../UPnP.cpp ../../Gzip.cpp ../../Timestamp.cpp ../../util.cpp \ + ../../i2pd.cpp HEADERS += DaemonQT.h mainwindow.h \ ../../HTTPServer.h ../../I2PControl.h ../../UPnP.h ../../Daemon.h ../../Config.h \