use std::string_view to extract params

This commit is contained in:
orignal 2025-07-05 21:06:49 -04:00
parent 1a6051e79b
commit 59d1695ee5
2 changed files with 103 additions and 98 deletions

View file

@ -11,6 +11,7 @@
#ifdef _MSC_VER
#include <stdlib.h>
#endif
#include <charconv>
#include "Base.h"
#include "Identity.h"
#include "Log.h"
@ -126,8 +127,7 @@ namespace client
if (separator)
{
separator++;
std::map<std::string, std::string> params;
ExtractParams (separator, params);
auto params = ExtractParams (separator);
auto it = params.find (SAM_PARAM_MAX);
if (it != params.end ())
maxver = it->second;
@ -172,7 +172,7 @@ namespace client
}
}
bool SAMSocket::IsSession(const std::string & id) const
bool SAMSocket::IsSession(std::string_view id) const
{
return id == m_ID;
}
@ -314,7 +314,7 @@ namespace client
}
}
static bool IsAcceptableSessionName(const std::string & str)
static bool IsAcceptableSessionName(std::string_view str)
{
auto itr = str.begin();
while(itr != str.end())
@ -330,11 +330,10 @@ namespace client
void SAMSocket::ProcessSessionCreate (char * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: Session create: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& style = params[SAM_PARAM_STYLE];
std::string& id = params[SAM_PARAM_ID];
std::string& destination = params[SAM_PARAM_DESTINATION];
auto params = ExtractParams (buf);
std::string_view style = params[SAM_PARAM_STYLE];
std::string_view id = params[SAM_PARAM_ID];
std::string_view destination = params[SAM_PARAM_DESTINATION];
if(!IsAcceptableSessionName(id))
{
@ -377,8 +376,10 @@ namespace client
return;
}
auto port = std::stoi(params[SAM_PARAM_PORT]);
if (port == -1)
uint16_t port = 0;
std::string_view p = params[SAM_PARAM_PORT];
auto res = std::from_chars(p.data(), p.data() + p.size(), port);
if (res.ec != std::errc())
{
SendSessionI2PError("Invalid port");
return;
@ -405,7 +406,7 @@ namespace client
}
// create destination
auto session = m_Owner.CreateSession (id, type, destination == SAM_VALUE_TRANSIENT ? "" : destination, &params);
auto session = m_Owner.CreateSession (id, type, destination == SAM_VALUE_TRANSIENT ? "" : destination, params);
if (session)
{
m_SocketType = eSAMSocketTypeSession;
@ -413,7 +414,13 @@ namespace client
{
session->UDPEndpoint = forward;
auto dest = session->GetLocalDestination ()->CreateDatagramDestination ();
auto port = forward ? std::stoi(params[SAM_PARAM_PORT]) : 0;
uint16_t port = 0;
if (forward)
{
std::string_view p = params[SAM_PARAM_PORT];
auto res = std::from_chars(p.data(), p.data() + p.size(), port);
if (res.ec != std::errc()) port = 0;
}
if (type == eSAMSessionTypeDatagram)
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
@ -486,11 +493,10 @@ namespace client
SendSessionI2PError ("Socket already in use");
return;
}
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
std::string& destination = params[SAM_PARAM_DESTINATION];
std::string& silent = params[SAM_PARAM_SILENT];
auto params = ExtractParams (buf);
std::string_view id = params[SAM_PARAM_ID];
std::string_view destination = params[SAM_PARAM_DESTINATION];
std::string_view silent = params[SAM_PARAM_SILENT];
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
m_ID = id;
auto session = m_Owner.FindSession (id);
@ -594,10 +600,9 @@ namespace client
SendSessionI2PError ("Socket already in use");
return;
}
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
std::string& silent = params[SAM_PARAM_SILENT];
auto params = ExtractParams (buf);
std::string_view id = params[SAM_PARAM_ID];
std::string_view silent = params[SAM_PARAM_SILENT];
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
m_ID = id;
auto session = m_Owner.FindSession (id);
@ -641,16 +646,14 @@ namespace client
{
LogPrint(eLogDebug, "SAM: Stream forward: ", buf);
std::map<std::string, std::string> params;
ExtractParams(buf, params);
auto params = ExtractParams(buf);
const auto itId = params.find(SAM_PARAM_ID);
if (itId == params.end())
{
SendSessionI2PError("Missing ID");
return;
}
const std::string& id = itId->second;
std::string_view id = itId->second;
auto session = m_Owner.FindSession(id);
if (!session)
@ -671,15 +674,16 @@ namespace client
return;
}
const std::string& portStr = itPort->second;
std::string_view portStr = itPort->second;
if (!std::all_of(portStr.begin(), portStr.end(), ::isdigit))
{
SendSessionI2PError("Port must be numeric");
return;
}
int port = std::stoi(portStr);
if (port <= 0 || port >= 0xFFFF)
uint16_t port = 0;
auto res = std::from_chars(portStr.data(), portStr.data() + portStr.size(), port);
if (res.ec != std::errc())
{
SendSessionI2PError("Invalid port");
return;
@ -729,9 +733,12 @@ namespace client
size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data)
{
LogPrint (eLogDebug, "SAM: Datagram send: ", buf, " ", len);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
size_t size = std::stoi(params[SAM_PARAM_SIZE]), offset = data - buf;
auto params = ExtractParams (buf);
size_t size = 0;
std::string_view sizeStr = params[SAM_PARAM_SIZE];
auto res = std::from_chars(sizeStr.data(), sizeStr.data() + sizeStr.size(), size);
if (res.ec != std::errc()) size = 0;
size_t offset = data - buf;
if (offset + size <= len)
{
auto session = m_Owner.FindSession(m_ID);
@ -764,8 +771,7 @@ namespace client
void SAMSocket::ProcessDestGenerate (char * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: Dest generate");
std::map<std::string, std::string> params;
ExtractParams (buf, params);
auto params = ExtractParams (buf);
// extract signature type
i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1;
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL;
@ -780,7 +786,7 @@ namespace client
{
try
{
cryptoType = std::stoi(it->second);
cryptoType = std::stoi(std::string (it->second));
}
catch (const std::exception& ex)
{
@ -801,9 +807,8 @@ namespace client
void SAMSocket::ProcessNamingLookup (char * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: Naming lookup: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& name = params[SAM_PARAM_NAME];
auto params = ExtractParams (buf);
std::string name (params[SAM_PARAM_NAME]);
std::shared_ptr<const i2p::data::IdentityEx> identity;
std::shared_ptr<const Address> addr;
auto session = m_Owner.FindSession(m_ID);
@ -848,16 +853,15 @@ namespace client
{
LogPrint (eLogDebug, "SAM: Subsession add: ", buf);
auto masterSession = std::static_pointer_cast<SAMMasterSession>(session);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
auto params = ExtractParams (buf);
std::string_view id = params[SAM_PARAM_ID];
if (masterSession->subsessions.count (id) > 1)
{
// session exists
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false);
return;
}
std::string& style = params[SAM_PARAM_STYLE];
std::string_view style = params[SAM_PARAM_STYLE];
SAMSessionType type = eSAMSessionTypeUnknown;
if (style == SAM_VALUE_STREAM) type = eSAMSessionTypeStream;
// TODO: implement other styles
@ -867,7 +871,7 @@ namespace client
SendSessionI2PError("Unsupported STYLE");
return;
}
auto fromPort = std::stoi(params[SAM_PARAM_FROM_PORT]);
auto fromPort = std::stoi(std::string (params[SAM_PARAM_FROM_PORT]));
if (fromPort == -1)
{
SendSessionI2PError("Invalid from port");
@ -876,7 +880,7 @@ namespace client
auto subsession = std::make_shared<SAMSubSession>(masterSession, id, type, fromPort);
if (m_Owner.AddSession (subsession))
{
masterSession->subsessions.insert (id);
masterSession->subsessions.insert (std::string (id));
SendSessionCreateReplyOk ();
}
else
@ -893,9 +897,8 @@ namespace client
{
LogPrint (eLogDebug, "SAM: Subsession remove: ", buf);
auto masterSession = std::static_pointer_cast<SAMMasterSession>(session);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
auto params = ExtractParams (buf);
std::string id(params[SAM_PARAM_ID]);
if (!masterSession->subsessions.erase (id))
{
SendMessageReply (SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), false);
@ -965,8 +968,9 @@ namespace client
SendMessageReply (m_Buffer, l, false);
}
void SAMSocket::ExtractParams (char * buf, std::map<std::string, std::string>& params)
const std::map<std::string_view, std::string_view> SAMSocket::ExtractParams (char * buf)
{
std::map<std::string_view, std::string_view> params;
char * separator;
do
{
@ -977,11 +981,12 @@ namespace client
{
*value = 0;
value++;
params[buf] = value;
params.emplace (buf, value);
}
buf = separator + 1;
}
while (separator);
return params;
}
void SAMSocket::Receive ()
@ -1289,7 +1294,7 @@ namespace client
boost::asio::post (m_Owner.GetService (), std::bind( !ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this()));
}
SAMSession::SAMSession (SAMBridge & parent, const std::string & id, SAMSessionType type):
SAMSession::SAMSession (SAMBridge & parent, std::string_view id, SAMSessionType type):
m_Bridge(parent), Name(id), Type (type), UDPEndpoint(nullptr)
{
}
@ -1302,7 +1307,7 @@ namespace client
}
}
SAMSingleSession::SAMSingleSession (SAMBridge & parent, const std::string & name, SAMSessionType type, std::shared_ptr<ClientDestination> dest):
SAMSingleSession::SAMSingleSession (SAMBridge & parent, std::string_view name, SAMSessionType type, std::shared_ptr<ClientDestination> dest):
SAMSession (parent, name, type),
localDestination (dest)
{
@ -1331,7 +1336,7 @@ namespace client
subsessions.clear ();
}
SAMSubSession::SAMSubSession (std::shared_ptr<SAMMasterSession> master, const std::string& name, SAMSessionType type, uint16_t port):
SAMSubSession::SAMSubSession (std::shared_ptr<SAMMasterSession> master, std::string_view name, SAMSessionType type, uint16_t port):
SAMSession (master->m_Bridge, name, type), masterSession (master), inPort (port)
{
if (Type == eSAMSessionTypeStream)
@ -1452,37 +1457,38 @@ namespace client
Accept ();
}
std::shared_ptr<SAMSession> SAMBridge::CreateSession (const std::string& id, SAMSessionType type,
const std::string& destination, const std::map<std::string, std::string> * params)
std::shared_ptr<SAMSession> SAMBridge::CreateSession (std::string_view id, SAMSessionType type,
std::string_view destination, const std::map<std::string_view, std::string_view>& params)
{
std::map<std::string, std::string> p(params.begin (), params.end ());
std::shared_ptr<ClientDestination> localDestination = nullptr;
if (destination != "")
{
i2p::data::PrivateKeys keys;
if (!keys.FromBase64 (destination)) return nullptr;
localDestination = m_IsSingleThread ?
i2p::client::context.CreateNewLocalDestination (GetIOService (), keys, true, params) :
i2p::client::context.CreateNewLocalDestination (keys, true, params);
i2p::client::context.CreateNewLocalDestination (GetIOService (), keys, true, &p) :
i2p::client::context.CreateNewLocalDestination (keys, true, &p);
}
else // transient
{
// extract signature type
i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1;
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL;
if (params)
if (!params.empty ())
{
auto it = params->find (SAM_PARAM_SIGNATURE_TYPE);
if (it != params->end ())
auto it = params.find (SAM_PARAM_SIGNATURE_TYPE);
if (it != params.end ())
{
if (!ResolveSignatureType (it->second, signatureType))
LogPrint (eLogWarning, "SAM: ", SAM_PARAM_SIGNATURE_TYPE, " is invalid ", it->second);
}
it = params->find (SAM_PARAM_CRYPTO_TYPE);
if (it != params->end ())
it = params.find (SAM_PARAM_CRYPTO_TYPE);
if (it != params.end ())
{
try
{
cryptoType = std::stoi(it->second);
cryptoType = std::stoi(std::string (it->second));
}
catch (const std::exception& ex)
{
@ -1491,8 +1497,8 @@ namespace client
}
}
localDestination = m_IsSingleThread ?
i2p::client::context.CreateNewLocalDestination (GetIOService (), true, signatureType, cryptoType, params) :
i2p::client::context.CreateNewLocalDestination (true, signatureType, cryptoType, params);
i2p::client::context.CreateNewLocalDestination (GetIOService (), true, signatureType, cryptoType, &p) :
i2p::client::context.CreateNewLocalDestination (true, signatureType, cryptoType, &p);
}
if (localDestination)
{
@ -1500,7 +1506,7 @@ namespace client
auto session = (type == eSAMSessionTypeMaster) ? std::make_shared<SAMMasterSession>(*this, id, localDestination) :
std::make_shared<SAMSingleSession>(*this, id, type, localDestination);
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto ret = m_Sessions.insert (std::make_pair(id, session));
auto ret = m_Sessions.emplace (id, session);
if (!ret.second)
LogPrint (eLogWarning, "SAM: Session ", id, " already exists");
return ret.first->second;
@ -1515,7 +1521,7 @@ namespace client
return ret.second;
}
void SAMBridge::CloseSession (const std::string& id)
void SAMBridge::CloseSession (std::string_view id)
{
std::shared_ptr<SAMSession> session;
{
@ -1565,7 +1571,7 @@ namespace client
// session's destructor is called here unless rescheduled
}
std::shared_ptr<SAMSession> SAMBridge::FindSession (const std::string& id) const
std::shared_ptr<SAMSession> SAMBridge::FindSession (std::string_view id) const
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto it = m_Sessions.find (id);
@ -1574,7 +1580,7 @@ namespace client
return nullptr;
}
std::list<std::shared_ptr<SAMSocket> > SAMBridge::ListSockets(const std::string & id) const
std::list<std::shared_ptr<SAMSocket> > SAMBridge::ListSockets(std::string_view id) const
{
std::list<std::shared_ptr<SAMSocket > > list;
{
@ -1654,25 +1660,23 @@ namespace client
LogPrint (eLogError, "SAM: Datagram receive error: ", ecode.message ());
}
bool SAMBridge::ResolveSignatureType (const std::string& name, i2p::data::SigningKeyType& type) const
bool SAMBridge::ResolveSignatureType (std::string_view name, i2p::data::SigningKeyType& type) const
{
try
auto res = std::from_chars(name.data(), name.data() + name.size(), type);
if (res.ec != std::errc())
{
type = std::stoi (name);
}
catch (const std::invalid_argument& ex)
{
// name is not numeric, resolving
auto it = m_SignatureTypes.find (name);
if (it != m_SignatureTypes.end ())
type = it->second;
if (res.ec == std::errc::invalid_argument)
{
// name is not numeric, resolving
auto it = m_SignatureTypes.find (name);
if (it != m_SignatureTypes.end ())
type = it->second;
else
return false;
}
else
return false;
}
catch (const std::exception& ex)
{
return false;
}
// name has been resolved
return true;
}

View file

@ -11,6 +11,7 @@
#include <inttypes.h>
#include <string>
#include <string_view>
#include <map>
#include <list>
#include <set>
@ -116,7 +117,7 @@ namespace client
void Terminate (const char* reason);
bool IsSession(const std::string & id) const;
bool IsSession(std::string_view id) const;
private:
@ -151,7 +152,7 @@ namespace client
void SendStreamI2PError(const std::string & msg);
void SendStreamCantReachPeer(const std::string & msg);
size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0
void ExtractParams (char * buf, std::map<std::string, std::string>& params);
const std::map<std::string_view, std::string_view> ExtractParams (char * buf);
void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote, std::shared_ptr<SAMSession> session = nullptr);
void HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet);
@ -199,7 +200,7 @@ namespace client
std::shared_ptr<boost::asio::ip::udp::endpoint> UDPEndpoint; // TODO: move
std::list<std::pair<std::shared_ptr<SAMSocket>, uint64_t> > acceptQueue; // socket, receive time in seconds
SAMSession (SAMBridge & parent, const std::string & name, SAMSessionType type);
SAMSession (SAMBridge & parent, std::string_view name, SAMSessionType type);
virtual ~SAMSession () {};
virtual std::shared_ptr<ClientDestination> GetLocalDestination () = 0;
@ -213,7 +214,7 @@ namespace client
{
std::shared_ptr<ClientDestination> localDestination;
SAMSingleSession (SAMBridge & parent, const std::string & name, SAMSessionType type, std::shared_ptr<ClientDestination> dest);
SAMSingleSession (SAMBridge & parent, std::string_view name, SAMSessionType type, std::shared_ptr<ClientDestination> dest);
~SAMSingleSession ();
std::shared_ptr<ClientDestination> GetLocalDestination () { return localDestination; };
@ -222,8 +223,8 @@ namespace client
struct SAMMasterSession: public SAMSingleSession
{
std::set<std::string> subsessions;
SAMMasterSession (SAMBridge & parent, const std::string & name, std::shared_ptr<ClientDestination> dest):
std::set<std::string, std::less<> > subsessions;
SAMMasterSession (SAMBridge & parent, std::string_view name, std::shared_ptr<ClientDestination> dest):
SAMSingleSession (parent, name, eSAMSessionTypeMaster, dest) {};
void Close ();
};
@ -233,7 +234,7 @@ namespace client
std::shared_ptr<SAMMasterSession> masterSession;
uint16_t inPort;
SAMSubSession (std::shared_ptr<SAMMasterSession> master, const std::string& name, SAMSessionType type, uint16_t port);
SAMSubSession (std::shared_ptr<SAMMasterSession> master, std::string_view name, SAMSessionType type, uint16_t port);
// implements SAMSession
std::shared_ptr<ClientDestination> GetLocalDestination ();
void StopLocalDestination ();
@ -250,13 +251,13 @@ namespace client
void Stop ();
auto& GetService () { return GetIOService (); };
std::shared_ptr<SAMSession> CreateSession (const std::string& id, SAMSessionType type, const std::string& destination, // empty string means transient
const std::map<std::string, std::string> * params);
std::shared_ptr<SAMSession> CreateSession (std::string_view id, SAMSessionType type, std::string_view destination, // empty string means transient
const std::map<std::string_view, std::string_view>& params);
bool AddSession (std::shared_ptr<SAMSession> session);
void CloseSession (const std::string& id);
std::shared_ptr<SAMSession> FindSession (const std::string& id) const;
void CloseSession (std::string_view id);
std::shared_ptr<SAMSession> FindSession (std::string_view id) const;
std::list<std::shared_ptr<SAMSocket> > ListSockets(const std::string & id) const;
std::list<std::shared_ptr<SAMSocket> > ListSockets(std::string_view id) const;
/** send raw data to remote endpoint from our UDP Socket */
void SendTo (const std::vector<boost::asio::const_buffer>& bufs, const boost::asio::ip::udp::endpoint& ep);
@ -264,7 +265,7 @@ namespace client
void AddSocket(std::shared_ptr<SAMSocket> socket);
void RemoveSocket(const std::shared_ptr<SAMSocket> & socket);
bool ResolveSignatureType (const std::string& name, i2p::data::SigningKeyType& type) const;
bool ResolveSignatureType (std::string_view name, i2p::data::SigningKeyType& type) const;
private:
@ -285,11 +286,11 @@ namespace client
boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint;
boost::asio::ip::udp::socket m_DatagramSocket;
mutable std::mutex m_SessionsMutex;
std::map<std::string, std::shared_ptr<SAMSession> > m_Sessions;
std::map<std::string, std::shared_ptr<SAMSession>, std::less<>> m_Sessions;
mutable std::mutex m_OpenSocketsMutex;
std::list<std::shared_ptr<SAMSocket> > m_OpenSockets;
uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1];
std::map<std::string, i2p::data::SigningKeyType> m_SignatureTypes;
const std::map<std::string_view, i2p::data::SigningKeyType> m_SignatureTypes;
public: