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 \