diff --git a/Config.cpp b/Config.cpp index d1bf7b64..9e9582ea 100644 --- a/Config.cpp +++ b/Config.cpp @@ -113,6 +113,7 @@ namespace config { ("log", value()->default_value(""), "Logs destination: stdout, file (stdout if not set, file - otherwise, for compatibility)") ("logfile", value()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)") ("loglevel", value()->default_value("info"), "Set the minimal level of log messages (debug, info, warn, error)") + ("datadir", value()->default_value(""), "Path to storage of i2pd data (RI, keys, peer profiles, ...)") ("host", value()->default_value("0.0.0.0"), "External IP") ("port", value()->default_value(0), "Port to listen for incoming connections (default: auto)") ("ipv6", value()->zero_tokens()->default_value(false), "Enable communication through ipv6") @@ -223,6 +224,15 @@ namespace config { void Finalize() { notify(m_Options); - }; + } + + bool IsDefault(const char *name) { + if (!m_Options.count(name)) + throw "try to check non-existent option"; + + if (m_Options[name].defaulted()) + return true; + return false; + } } // namespace config } // namespace i2p diff --git a/Config.h b/Config.h index 51a12d70..07d7ccb7 100644 --- a/Config.h +++ b/Config.h @@ -94,6 +94,13 @@ namespace config { notify(m_Options); return true; } + + /** + * @brief Check is value explicitly given or default + * @param name Name of checked parameter + * @return true if value set to default, false othervise + */ + bool IsDefault(const char *name); } } diff --git a/Daemon.cpp b/Daemon.cpp index b9d8bd44..e972cee3 100644 --- a/Daemon.cpp +++ b/Daemon.cpp @@ -83,14 +83,14 @@ namespace i2p LogPrint(eLogDebug, "FS: data directory: ", datadir); uint16_t port; i2p::config::GetOption("port", port); - if (port) + if (!i2p::config::IsDefault("port")) { LogPrint(eLogInfo, "Daemon: accepting incoming connections at port ", port); i2p::context.UpdatePort (port); } std::string host; i2p::config::GetOption("host", host); - if (host != "0.0.0.0") + if (!i2p::config::IsDefault("host")) { LogPrint(eLogInfo, "Daemon: setting address for incoming connections to ", host); i2p::context.UpdateAddress (boost::asio::ip::address::from_string (host)); @@ -125,7 +125,10 @@ namespace i2p i2p::context.SetExtraBandwidth (); } else + { + LogPrint(eLogInfo, "Daemon: bandwidth set to 'low'"); i2p::context.SetLowBandwidth (); + } return true; } diff --git a/Datagram.cpp b/Datagram.cpp index b944cf11..9221824d 100644 --- a/Datagram.cpp +++ b/Datagram.cpp @@ -66,7 +66,7 @@ namespace datagram msgs.push_back (i2p::tunnel::TunnelMessageBlock { i2p::tunnel::eDeliveryTypeTunnel, - leases[i].tunnelGateway, leases[i].tunnelID, + leases[i]->tunnelGateway, leases[i]->tunnelID, garlic }); outboundTunnel->SendTunnelDataMsg (msgs); diff --git a/Destination.cpp b/Destination.cpp index c07718cb..e7e9b93c 100644 --- a/Destination.cpp +++ b/Destination.cpp @@ -303,7 +303,7 @@ namespace client } else { - LogPrint (eLogError, "New remote LeaseSet verification failed"); + LogPrint (eLogError, "New remote LeaseSet failed"); leaseSet = nullptr; } } @@ -678,7 +678,7 @@ namespace client auto ts = i2p::util::GetMillisecondsSinceEpoch (); for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();) { - if (ts > it->second->GetExpirationTime ()) // leaseset expired + if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired { LogPrint (eLogWarning, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired"); it = m_RemoteLeaseSets.erase (it); diff --git a/I2NPProtocol.cpp b/I2NPProtocol.cpp index c31a4479..db3330e2 100644 --- a/I2NPProtocol.cpp +++ b/I2NPProtocol.cpp @@ -265,9 +265,9 @@ namespace i2p auto leases = leaseSet->GetNonExpiredLeases (); if (leases.size () > 0) { - htobe32buf (payload + size, leases[0].tunnelID); + htobe32buf (payload + size, leases[0]->tunnelID); size += 4; // reply tunnelID - memcpy (payload + size, leases[0].tunnelGateway, 32); + memcpy (payload + size, leases[0]->tunnelGateway, 32); size += 32; // reply tunnel gateway } else diff --git a/LeaseSet.cpp b/LeaseSet.cpp index adcbc388..ebbdbd97 100644 --- a/LeaseSet.cpp +++ b/LeaseSet.cpp @@ -69,7 +69,6 @@ namespace data void LeaseSet::Update (const uint8_t * buf, size_t len) { - m_Leases.clear (); if (len > m_BufferLen) { auto oldBuffer = m_Buffer; @@ -84,7 +83,6 @@ namespace data void LeaseSet::PopulateLeases () { m_StoreLeases = true; - m_Leases.clear (); ReadFromBuffer (false); } @@ -112,8 +110,16 @@ namespace data return; } + // reset existing leases + if (m_StoreLeases) + for (auto it: m_Leases) + it->isUpdated = false; + else + m_Leases.clear (); + // process leases m_ExpirationTime = 0; + auto ts = i2p::util::GetMillisecondsSinceEpoch (); const uint8_t * leases = m_Buffer + size; for (int i = 0; i < num; i++) { @@ -124,21 +130,48 @@ namespace data leases += 4; // tunnel ID lease.endDate = bufbe64toh (leases); leases += 8; // end date - if (lease.endDate > m_ExpirationTime) - m_ExpirationTime = lease.endDate; - if (m_StoreLeases) + if (ts < lease.endDate) { - m_Leases.push_back (lease); - // check if lease's gateway is in our netDb - if (!netdb.FindRouter (lease.tunnelGateway)) - { - // if not found request it - LogPrint (eLogInfo, "LeaseSet: Lease's tunnel gateway not found, requesting"); - netdb.RequestDestination (lease.tunnelGateway); - } - } + if (lease.endDate > m_ExpirationTime) + m_ExpirationTime = lease.endDate; + if (m_StoreLeases) + { + auto ret = m_Leases.insert (std::make_shared(lease)); + if (!ret.second) *(*ret.first) = lease; // update existing + (*ret.first)->isUpdated = true; + // check if lease's gateway is in our netDb + if (!netdb.FindRouter (lease.tunnelGateway)) + { + // if not found request it + LogPrint (eLogInfo, "LeaseSet: Lease's tunnel gateway not found, requesting"); + netdb.RequestDestination (lease.tunnelGateway); + } + } + } + else + LogPrint (eLogWarning, "LeaseSet: Lease is expired already "); } - + if (!m_ExpirationTime) + { + LogPrint (eLogWarning, "LeaseSet: all leases are expired. Dropped"); + m_IsValid = false; + return; + } + // delete old leases + if (m_StoreLeases) + { + for (auto it = m_Leases.begin (); it != m_Leases.end ();) + { + if (!(*it)->isUpdated) + { + (*it)->endDate = 0; // somebody might still hold it + m_Leases.erase (it++); + } + else + it++; + } + } + // verify if (!m_Identity->Verify (m_Buffer, leases - m_Buffer, leases)) { @@ -147,13 +180,13 @@ namespace data } } - const std::vector LeaseSet::GetNonExpiredLeases (bool withThreshold) const + const std::vector > LeaseSet::GetNonExpiredLeases (bool withThreshold) const { auto ts = i2p::util::GetMillisecondsSinceEpoch (); - std::vector leases; - for (auto& it: m_Leases) + std::vector > leases; + for (auto it: m_Leases) { - auto endDate = it.endDate; + auto endDate = it->endDate; if (!withThreshold) endDate -= i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD*1000; if (ts < endDate) @@ -165,13 +198,14 @@ namespace data bool LeaseSet::HasExpiredLeases () const { auto ts = i2p::util::GetMillisecondsSinceEpoch (); - for (auto& it: m_Leases) - if (ts >= it.endDate) return true; + for (auto it: m_Leases) + if (ts >= it->endDate) return true; return false; } bool LeaseSet::IsExpired () const { + if (IsEmpty ()) return true; auto ts = i2p::util::GetMillisecondsSinceEpoch (); return ts > m_ExpirationTime; } diff --git a/LeaseSet.h b/LeaseSet.h index 0f437170..dd94bfaf 100644 --- a/LeaseSet.h +++ b/LeaseSet.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "Identity.h" namespace i2p @@ -20,15 +21,19 @@ namespace data { IdentHash tunnelGateway; uint32_t tunnelID; - uint64_t endDate; + uint64_t endDate; // 0 means invalid + bool isUpdated; // trasient + }; - bool operator< (const Lease& other) const - { - if (endDate != other.endDate) - return endDate > other.endDate; + struct LeaseCmp + { + bool operator() (std::shared_ptr l1, std::shared_ptr l2) const + { + if (l1->tunnelID != l2->tunnelID) + return l1->tunnelID < l2->tunnelID; else - return tunnelID < other.tunnelID; - } + return l1->tunnelGateway < l2->tunnelGateway; + }; }; const int MAX_LS_BUFFER_SIZE = 3072; @@ -41,20 +46,20 @@ namespace data LeaseSet (std::shared_ptr pool); ~LeaseSet () { delete[] m_Buffer; }; void Update (const uint8_t * buf, size_t len); - void PopulateLeases (); /// from buffer + void PopulateLeases (); // from buffer std::shared_ptr GetIdentity () const { return m_Identity; }; const uint8_t * GetBuffer () const { return m_Buffer; }; size_t GetBufferLen () const { return m_BufferLen; }; bool IsValid () const { return m_IsValid; }; - - // implements RoutingDestination - const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); }; - const std::vector& GetLeases () const { return m_Leases; }; - const std::vector GetNonExpiredLeases (bool withThreshold = true) const; + const std::vector > GetNonExpiredLeases (bool withThreshold = true) const; bool HasExpiredLeases () const; bool IsExpired () const; + bool IsEmpty () const { return m_Leases.empty (); }; uint64_t GetExpirationTime () const { return m_ExpirationTime; }; + + // implements RoutingDestination + const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); }; const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; }; bool IsDestination () const { return true; }; @@ -65,7 +70,7 @@ namespace data private: bool m_IsValid, m_StoreLeases; // we don't need to store leases for floodfill - std::vector m_Leases; + std::set, LeaseCmp> m_Leases; uint64_t m_ExpirationTime; // in milliseconds std::shared_ptr m_Identity; uint8_t m_EncryptionKey[256]; diff --git a/NTCPSession.cpp b/NTCPSession.cpp index ed987bb9..81b6bdc2 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -736,7 +736,7 @@ namespace transport { if (ecode != boost::asio::error::operation_aborted) { - LogPrint (eLogWarning, "NTCP: No activity fo ", NTCP_TERMINATION_TIMEOUT, " seconds"); + LogPrint (eLogWarning, "NTCP: No activity for ", NTCP_TERMINATION_TIMEOUT, " seconds"); //Terminate (); m_Socket.close ();// invoke Terminate () from HandleReceive } diff --git a/RouterContext.cpp b/RouterContext.cpp index 962cf3ee..3dc79027 100644 --- a/RouterContext.cpp +++ b/RouterContext.cpp @@ -48,7 +48,7 @@ namespace i2p if (!port) port = rand () % (30777 - 9111) + 9111; // I2P network ports range std::string host; i2p::config::GetOption("host", host); - if (host == "0.0.0.0") + if (i2p::config::IsDefault("host")) host = "127.0.0.1"; // replace default address with safe value routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); routerInfo.AddNTCPAddress (host.c_str(), port); diff --git a/Streaming.cpp b/Streaming.cpp index 8e0b223d..0c1a5e5d 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -22,7 +22,6 @@ namespace stream { RAND_bytes ((uint8_t *)&m_RecvStreamID, 4); m_RemoteIdentity = remote->GetIdentity (); - m_CurrentRemoteLease.endDate = 0; } Stream::Stream (boost::asio::io_service& service, StreamingDestination& local): @@ -599,9 +598,9 @@ namespace stream } auto ts = i2p::util::GetMillisecondsSinceEpoch (); - if (!m_CurrentRemoteLease.endDate || ts >= m_CurrentRemoteLease.endDate - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD*1000) + if (!m_CurrentRemoteLease || ts >= m_CurrentRemoteLease->endDate - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD*1000) UpdateCurrentRemoteLease (true); - if (ts < m_CurrentRemoteLease.endDate) + if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate) { std::vector msgs; for (auto it: packets) @@ -610,7 +609,7 @@ namespace stream msgs.push_back (i2p::tunnel::TunnelMessageBlock { i2p::tunnel::eDeliveryTypeTunnel, - m_CurrentRemoteLease.tunnelGateway, m_CurrentRemoteLease.tunnelID, + m_CurrentRemoteLease->tunnelGateway, m_CurrentRemoteLease->tunnelID, msg }); m_NumSentBytes += it->GetLength (); @@ -705,11 +704,11 @@ namespace stream void Stream::UpdateCurrentRemoteLease (bool expired) { - if (!m_RemoteLeaseSet) + if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ()) { m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ()); - if (!m_RemoteLeaseSet) - LogPrint (eLogError, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " not found"); + if (!m_RemoteLeaseSet) + LogPrint (eLogWarning, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " not found"); } if (m_RemoteLeaseSet) { @@ -725,10 +724,10 @@ namespace stream if (!leases.empty ()) { bool updated = false; - if (expired) + if (expired && m_CurrentRemoteLease) { for (auto it: leases) - if ((it.tunnelGateway == m_CurrentRemoteLease.tunnelGateway) && (it.tunnelID != m_CurrentRemoteLease.tunnelID)) + if ((it->tunnelGateway == m_CurrentRemoteLease->tunnelGateway) && (it->tunnelID != m_CurrentRemoteLease->tunnelID)) { m_CurrentRemoteLease = it; updated = true; @@ -738,7 +737,7 @@ namespace stream if (!updated) { uint32_t i = rand () % leases.size (); - if (m_CurrentRemoteLease.endDate && leases[i].tunnelID == m_CurrentRemoteLease.tunnelID) + if (m_CurrentRemoteLease && leases[i]->tunnelID == m_CurrentRemoteLease->tunnelID) // make sure we don't select previous i = (i + 1) % leases.size (); // if so, pick next m_CurrentRemoteLease = leases[i]; @@ -747,12 +746,12 @@ namespace stream else { m_RemoteLeaseSet = nullptr; - m_CurrentRemoteLease.endDate = 0; + m_CurrentRemoteLease = nullptr; // re-request expired } } else - m_CurrentRemoteLease.endDate = 0; + m_CurrentRemoteLease = nullptr; } std::shared_ptr Stream::CreateDataMessage (const uint8_t * payload, size_t len) @@ -879,7 +878,7 @@ namespace stream it->second.push_back (packet); else { - m_SavedPackets.emplace (receiveStreamID, std::list{ packet }); + m_SavedPackets[receiveStreamID] = std::list{ packet }; auto timer = std::make_shared (m_Owner->GetService ()); timer->expires_from_now (boost::posix_time::seconds(PENDING_INCOMING_TIMEOUT)); auto s = shared_from_this (); diff --git a/Streaming.h b/Streaming.h index 0b240c0b..2f85397a 100644 --- a/Streaming.h +++ b/Streaming.h @@ -172,7 +172,7 @@ namespace stream std::shared_ptr m_RemoteIdentity; std::shared_ptr m_RemoteLeaseSet; std::shared_ptr m_RoutingSession; - i2p::data::Lease m_CurrentRemoteLease; + std::shared_ptr m_CurrentRemoteLease; std::shared_ptr m_CurrentOutboundTunnel; std::queue m_ReceiveQueue; std::set m_SavedPackets; diff --git a/Tunnel.cpp b/Tunnel.cpp index dbe87a2f..f7102327 100644 --- a/Tunnel.cpp +++ b/Tunnel.cpp @@ -666,6 +666,10 @@ namespace tunnel { // trying to create one more inbound tunnel auto router = i2p::data::netdb.GetRandomRouter (); + if (!router) { + LogPrint (eLogWarning, "Tunnel: can't find any router, skip creating tunnel"); + return; + } LogPrint (eLogDebug, "Tunnel: creating one hop inbound tunnel"); CreateTunnel ( std::make_shared (std::vector > { router->GetRouterIdentity () }) diff --git a/debian/i2pd.init b/debian/i2pd.init index d87aa000..8cfee8d4 100644 --- a/debian/i2pd.init +++ b/debian/i2pd.init @@ -18,6 +18,7 @@ DAEMON_OPTS="" # Arguments to run the daemon with PIDFILE=/var/run/$NAME.pid I2PCONF=/etc/$NAME/i2pd.conf TUNCONF=/etc/$NAME/tunnels.conf +LOGFILE=/var/log/$NAME.log USER="i2pd" # Exit if the package is not installed @@ -43,10 +44,13 @@ do_start() touch "$PIDFILE" chown -f $USER:adm "$PIDFILE" + touch "$LOGFILE" + chown -f $USER:adm "$LOGFILE" + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --chuid "$USER" --test > /dev/null \ || return 1 start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --chuid "$USER" -- \ - --service --daemon --log --conf=$I2PCONF --tunconf=$TUNCONF \ + --service --daemon --log=file --logfile=$LOGFILE --conf=$I2PCONF --tunconf=$TUNCONF \ --port=$I2PD_PORT $DAEMON_OPTS > /dev/null 2>&1 \ || return 2 return $? diff --git a/debian/i2pd.upstart b/debian/i2pd.upstart index d1536ea3..29c6cbdb 100644 --- a/debian/i2pd.upstart +++ b/debian/i2pd.upstart @@ -6,5 +6,6 @@ stop on runlevel [016] or unmounting-filesystem # these can be overridden in /etc/init/i2pd.override env I2PD_HOST="1.2.3.4" env I2PD_PORT="4567" +env LOGFILE="/var/log/i2pd.log" -exec /usr/sbin/i2pd --daemon --log --host=$I2PD_HOST --port=$I2PD_PORT +exec /usr/sbin/i2pd --daemon --log=file --logfile=$LOGFILE --service --host=$I2PD_HOST --port=$I2PD_PORT diff --git a/docs/configuration.md b/docs/configuration.md index 3f3c5386..d5a3a5b0 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -12,6 +12,7 @@ Command line options * --log= - Logs destination: stdout, file (stdout if not set, file - otherwise, for compatibility) * --logfile= - Path to logfile (default - autodetect) * --loglevel= - Log messages above this level (debug, *info, warn, error) +* --datadir= - Path to storage of i2pd data (RI, keys, peer profiles, ...) * --host= - The external IP * --port= - The port to listen on * --daemon - Router will go to background after start