diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index 6d89c0a3..315173c3 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include "Config.h" @@ -631,6 +632,50 @@ namespace client LogPrint (eLogInfo, "Clients: ", numServerTunnels, " I2P server tunnels created"); } + void ClientContext::AddSection (boost::property_tree::ptree& pt, const std::string& name, + const std::string& chunk, int& numSections) + { + if (chunk.empty ()) return; + boost::property_tree::ptree section; + try + { + std::stringstream ss (chunk); + boost::property_tree::read_ini (ss, section); + } + catch (std::exception& ex) + { + LogPrint (eLogWarning, "Clients: Skipped ", name.empty () ? "tunnels config header" : name, ": ", ex.what ()); + return; + } + for (auto& it: section) + pt.push_back (it); + numSections++; + } + + // Fallback for a file rejected as a whole: a single malformed line makes read_ini throw + // and every tunnel is lost, including sections that are correct. Read section by section + // instead, so that only unreadable ones are skipped + int ClientContext::ReadTunnelsBySection (const std::string& tunConf, boost::property_tree::ptree& pt) + { + std::ifstream f (tunConf); + if (!f.is_open ()) return 0; + int numSections = 0; + std::string line, chunk, name; + while (std::getline (f, line)) + { + auto start = line.find_first_not_of (" \t"); + if (start != std::string::npos && line[start] == '[') + { + AddSection (pt, name, chunk, numSections); + chunk.clear (); + name = line.substr (start); + } + chunk += line; chunk += "\n"; + } + AddSection (pt, name, chunk, numSections); + return numSections; + } + void ClientContext::ReadTunnels (const std::string& tunConf, int& numClientTunnels, int& numServerTunnels) { boost::property_tree::ptree pt; @@ -638,7 +683,10 @@ namespace client boost::property_tree::read_ini (tunConf, pt); } catch (std::exception& ex) { LogPrint (eLogWarning, "Clients: Can't read ", tunConf, ": ", ex.what ()); - return; + pt.clear (); + int numSections = ReadTunnelsBySection (tunConf, pt); + if (!numSections) return; + LogPrint (eLogWarning, "Clients: ", tunConf, " read section by section, ", numSections, " section(s) accepted"); } std::map > destinations; // keys -> destination diff --git a/libi2pd_client/ClientContext.h b/libi2pd_client/ClientContext.h index 09003cde..a726d29c 100644 --- a/libi2pd_client/ClientContext.h +++ b/libi2pd_client/ClientContext.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "Destination.h" #include "I2PService.h" #include "I2PTunnel.h" @@ -133,6 +134,8 @@ namespace client void ReadTunnels (); void ReadTunnels (const std::string& tunConf, int& numClientTunnels, int& numServerTunnels); + int ReadTunnelsBySection (const std::string& tunConf, boost::property_tree::ptree& pt); + void AddSection (boost::property_tree::ptree& pt, const std::string& name, const std::string& chunk, int& numSections); void ReadHttpProxy (); void ReadSocksProxy (); // take the proxy out under the lock, so it can be stopped without holding it