From 345e85ea30e93d5df42e3d0a63eb5badd4270ee5 Mon Sep 17 00:00:00 2001 From: acetone <63557806+freeacetone@users.noreply.github.com> Date: Sat, 20 Jun 2026 15:26:41 +0300 Subject: [PATCH] SAM: resolve datagram destinations like streams, supporting b32 and b33 hosts --- libi2pd_client/SAM.cpp | 106 ++++++++++++++++++++++------------------- libi2pd_client/SAM.h | 5 ++ 2 files changed, 62 insertions(+), 49 deletions(-) diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 34c24f54..e31deb92 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -12,6 +12,7 @@ #include #endif #include +#include #include "Base.h" #include "Identity.h" #include "Log.h" @@ -779,18 +780,7 @@ namespace client auto session = m_Owner.FindSession(m_ID); if (session) { - auto d = session->GetLocalDestination ()->GetDatagramDestination (); - if (d) - { - i2p::data::IdentityEx dest; - dest.FromBase64 (params[SAM_PARAM_DESTINATION]); - if (session->Type == SAMSessionType::eSAMSessionTypeDatagram) - d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ()); - else // raw - d->SendRawDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ()); - } - else - LogPrint (eLogError, "SAM: Missing datagram destination"); + session->SendDatagram (params[SAM_PARAM_DESTINATION], (const uint8_t *)data, size, 0, 0); } else LogPrint (eLogError, "SAM: Session is not created from DATAGRAM SEND"); @@ -1365,6 +1355,60 @@ namespace client } } + void SAMSession::SendDatagram (std::string_view destination, const uint8_t * payload, size_t len, + uint16_t fromPort, uint16_t toPort) + { + auto localDest = GetLocalDestination (); + auto datagramDest = localDest ? localDest->GetDatagramDestination () : nullptr; + if (!datagramDest) + { + LogPrint (eLogError, "SAM: Missing datagram destination for session ", Name); + return; + } + // Resolve like a stream connect: a full base64 destination, or a .i2p host + // that is either a plain b32 (ident hash) or a blinded b33 (encrypted leaseset). + std::shared_ptr addr; + if (destination.find (".i2p") != std::string_view::npos) + addr = context.GetAddressBook ().GetAddress (std::string (destination)); + else + { + auto dest = std::make_shared (); + if (dest->FromBase64 (std::string (destination)) > 0) + { + context.GetAddressBook ().InsertFullAddress (dest); + addr = std::make_shared
(dest->GetIdentHash ()); + } + } + if (!addr || !addr->IsValid ()) + { + LogPrint (eLogError, "SAM: Datagram invalid destination ", destination); + return; + } + auto sendTo = [datagramDest, type = Type, fromPort, toPort] + (const uint8_t * p, size_t l, const i2p::data::IdentHash& ident) + { + if (type == SAMSessionType::eSAMSessionTypeDatagram) + datagramDest->SendDatagramTo (p, l, ident, fromPort, toPort); + else if (type == SAMSessionType::eSAMSessionTypeRaw) + datagramDest->SendRawDatagramTo (p, l, ident, fromPort, toPort); + else + LogPrint (eLogError, "SAM: Unexpected session type ", (int)type, " for datagram send"); + }; + if (addr->IsIdentHash ()) + sendTo (payload, len, addr->identHash); // plain b32 / full dest: send straight away + else + { + // b33: the leaseset lookup is async and the receive buffer is reused, so copy + // the payload, then send to the resolved ident hash once the leaseset arrives. + auto data = std::make_shared > (payload, payload + len); + localDest->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, + [sendTo, data](std::shared_ptr ls) + { + if (ls) sendTo (data->data (), data->size (), ls->GetIdentHash ()); + }); + } + } + SAMSingleSession::SAMSingleSession (SAMBridge & parent, std::string_view name, SAMSessionType type, std::shared_ptr dest): SAMSession (parent, name, type), localDestination (dest) @@ -1691,43 +1735,7 @@ namespace client LogPrint (eLogInfo, "SAM: Datagram params are FROM_PORT=", fromPort, " TO_PORT=", toPort); } - auto localDest = session->GetLocalDestination (); - auto datagramDest = localDest ? localDest->GetDatagramDestination () : nullptr; - if (datagramDest) - { - i2p::data::IdentHash ident; bool isDest = false; - if (std::string_view (destination).find(".i2p") != std::string_view::npos) - { - auto addr = context.GetAddressBook().GetAddress (destination); - if (addr && addr->IsValid () && addr->IsIdentHash ()) - { - ident = addr->identHash; - isDest = true; - } - } - else - { - i2p::data::IdentityEx dest; - if (dest.FromBase64 (destination) > 0) - { - ident = dest.GetIdentHash (); - isDest = true; - } - } - if (isDest) - { - if (session->Type == SAMSessionType::eSAMSessionTypeDatagram) - datagramDest->SendDatagramTo ((uint8_t *)eol, payloadLen, ident, fromPort, toPort); - else if (session->Type == SAMSessionType::eSAMSessionTypeRaw) - datagramDest->SendRawDatagramTo ((uint8_t *)eol, payloadLen, ident, fromPort, toPort); - else - LogPrint (eLogError, "SAM: Unexpected session type ", (int)session->Type, "for session ", sessionID); - } - else - LogPrint (eLogError, "SAM: Datagram unexpected destination ", destination); - } - else - LogPrint (eLogError, "SAM: Datagram destination is not set for session ", sessionID); + session->SendDatagram (destination, (const uint8_t *)eol, payloadLen, fromPort, toPort); } else LogPrint (eLogError, "SAM: Session ", sessionID, " not found"); diff --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h index fa6841db..2be2899d 100644 --- a/libi2pd_client/SAM.h +++ b/libi2pd_client/SAM.h @@ -225,6 +225,11 @@ namespace client virtual void Close () { CloseStreams (); }; void CloseStreams (); + // Resolves a datagram destination - a full base64 destination, a plain b32 + // or a blinded b33 .i2p host - the same way a stream connect does, then + // sends the payload. A b33 lookup is async, so the payload is copied. + void SendDatagram (std::string_view destination, const uint8_t * payload, size_t len, + uint16_t fromPort, uint16_t toPort); }; struct SAMSingleSession: public SAMSession