fixup docs

This commit is contained in:
eyedeekay
2025-10-09 13:50:00 -04:00
parent 21239a4376
commit 15a26614af
14 changed files with 1485 additions and 670 deletions

View File

@@ -2,6 +2,34 @@
--
import "github.com/go-i2p/go-sam-go/common"
Package common provides core SAM protocol implementation and shared utilities
for I2P.
This package implements the foundational SAMv3.3 protocol communication layer,
including session management, configuration options, I2P name resolution, and
shared abstractions used by all session types (stream, datagram, raw).
Core types:
- SAM: Base connection to I2P SAM bridge (default port 7656)
- Session: Base session interface with lifecycle management
- I2PConfig: Configuration builder for tunnel parameters
- SAMEmit: SAM protocol command formatter
Session creation requires 2-5 minutes for I2P tunnel establishment; use generous
timeouts and exponential backoff retry logic. All network operations should use
context.Context for cancellation and timeout control.
Basic usage:
sam, err := common.NewSAM("127.0.0.1:7656")
if err != nil { log.Fatal(err) }
defer sam.Close()
session, err := sam.NewGenericSession("STREAM", "my-session", keys, []string{"inbound.length=1"})
defer session.Close()
This package is primarily used as a foundation by higher-level packages (stream,
datagram, raw, primary) rather than being used directly by applications.
## Usage

View File

@@ -2,6 +2,37 @@
--
import "github.com/go-i2p/go-sam-go/datagram"
Package datagram provides legacy authenticated datagram sessions for I2P using
SAMv3 DATAGRAM.
DATAGRAM sessions provide authenticated, repliable UDP-like messaging over I2P
tunnels. This is the legacy format without replay protection. For new
applications requiring replay protection, use package datagram2 instead.
Key features:
- Authenticated datagrams with signature verification
- Repliable (can send replies to sender)
- No replay protection (use datagram2 if needed)
- UDP-like messaging (unreliable, unordered)
- Maximum 31744 bytes per datagram (11 KB recommended)
- Implements net.PacketConn interface
Session creation requires 2-5 minutes for I2P tunnel establishment. Use generous
timeouts and exponential backoff retry logic.
Basic usage:
sam, err := common.NewSAM("127.0.0.1:7656")
session, err := datagram.NewDatagramSession(sam, "my-session", keys, []string{"inbound.length=1"})
defer session.Close()
conn := session.PacketConn()
n, err := conn.WriteTo(data, destination)
n, addr, err := conn.ReadFrom(buffer)
See also: Package datagram2 (with replay protection), datagram3
(unauthenticated), stream (TCP-like), raw (non-repliable), primary
(multi-session management).
## Usage

View File

@@ -2,74 +2,37 @@
--
import "github.com/go-i2p/go-sam-go/datagram2"
Package datagram2 provides authenticated datagram2 sessions with replay
Package datagram2 provides authenticated datagram sessions with replay
protection for I2P.
DATAGRAM2 is a new format specified in early 2025 that replaces legacy DATAGRAM
sessions for applications requiring replay protection. It uses the SAMv3
protocol with STYLE=DATAGRAM2.
DATAGRAM2 sessions provide authenticated, repliable UDP-like messaging over I2P
tunnels with replay attack protection. This is the recommended datagram format
for applications requiring both source authentication and replay protection.
# Key Features
Key features:
- Authenticated datagrams with signature verification
- Replay protection (not available in legacy DATAGRAM)
- Offline signature support
- Repliable (can send replies to sender)
- UDP-like messaging (unreliable, unordered)
- Maximum 31744 bytes (11 KB recommended for reliability)
- Compatible with SAMv3.3 PRIMARY sessions
- Maximum 31744 bytes per datagram (11 KB recommended)
- Implements net.PacketConn interface
# Basic Usage
Session creation requires 2-5 minutes for I2P tunnel establishment. Use generous
timeouts and exponential backoff retry logic.
Create a session:
Basic usage:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
sam, err := common.NewSAM("127.0.0.1:7656")
session, err := datagram2.NewDatagram2Session(sam, "my-session", keys, []string{"inbound.length=1"})
defer session.Close()
Send and receive using PacketConn:
conn := session.PacketConn()
defer conn.Close()
n, err := conn.WriteTo(data, destination)
n, addr, err := conn.ReadFrom(buffer)
# I2P Timing Considerations
Session creation: 2-5 minutes for I2P tunnel establishment
Message delivery: Variable latency (network-dependent)
Recommended: Use generous timeouts (5+ minutes for session creation) and retry
logic with exponential backoff.
# Implementation Status
DATAGRAM2 specification finalized early 2025. This is one of the first
implementations. Check I2P router documentation for SAMv3 DATAGRAM2 support
(Java I2P 0.9.x+, i2pd 2.x+).
Current implementation status:
- Core session management: ✅ Implemented
- UDP forwarding: ✅ Implemented
- Send/receive operations: ✅ Implemented
- PacketConn interface: ✅ Implemented
- Replay protection: ✅ Implemented (handled by I2P router)
- PRIMARY session integration: ⏸️ Deferred (low priority)
# See Also
Package datagram: Legacy DATAGRAM sessions (authenticated, no replay protection)
Package datagram3: DATAGRAM3 sessions (unauthenticated, hash-based sources)
Package stream: TCP-like reliable connections
Package raw: Encrypted but unauthenticated datagrams (non-repliable)
Package primary: PRIMARY session management for multiple subsessions
See also: Package datagram (legacy, no replay protection), datagram3
(unauthenticated), stream (TCP-like), raw (non-repliable), primary
(multi-session management).
## Usage
@@ -362,30 +325,11 @@ Example usage:
func NewDatagram2Session(sam *common.SAM, id string, keys i2pkeys.I2PKeys, options []string) (*Datagram2Session, error)
```
NewDatagram2Session creates a new datagram2 session with replay protection for
UDP-like I2P messaging. This function establishes a new DATAGRAM2 session with
the provided SAM connection, session ID, cryptographic keys, and configuration
options. It automatically creates a UDP listener for receiving forwarded
datagrams (SAMv3 requirement) and configures the session with PORT/HOST
parameters.
DATAGRAM2 provides enhanced security compared to legacy DATAGRAM:
- Replay protection prevents replay attacks (not available in DATAGRAM)
- Offline signature support for advanced key management
- Identical SAM API for easy migration from DATAGRAM
I2P Timing Considerations:
- Session creation can take 2-5 minutes for I2P tunnel establishment
- Use context.WithTimeout with generous timeouts (5+ minutes recommended)
- Implement exponential backoff retry logic for connection attempts
- Distinguish between I2P timing delays and actual failures
Example usage:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
session, err := NewDatagram2Session(sam, "my-session", keys, []string{"inbound.length=1"})
UDP-like I2P messaging. It initializes the session with the provided SAM
connection, session ID, cryptographic keys, and configuration options. The
session automatically creates a UDP listener for receiving forwarded datagrams
per SAMv3 requirements. Example usage: session, err := NewDatagram2Session(sam,
"my-session", keys, []string{"inbound.length=1"})
#### func NewDatagram2SessionFromSubsession
@@ -397,34 +341,11 @@ that has already been registered with a PRIMARY session using SESSION ADD. This
constructor skips the session creation step since the subsession is already
registered with the SAM bridge.
This function is specifically designed for use with SAMv3.3 PRIMARY sessions
where subsessions are created using SESSION ADD rather than SESSION CREATE
commands.
For PRIMARY datagram2 subsessions, UDP forwarding is mandatory (SAMv3
requirement). The UDP connection must be provided for proper datagram reception
via UDP forwarding.
requirement). The UDP connection must be provided for proper datagram reception.
DATAGRAM2 subsessions share the same cryptographic keys and I2P tunnels as the
PRIMARY session, but can be differentiated using LISTEN_PORT for incoming
datagram routing.
Parameters:
- sam: SAM connection for data operations (separate from the primary session's control connection)
- id: The subsession ID that was already registered with SESSION ADD
- keys: The I2P keys from the primary session (shared across all subsessions)
- options: Configuration options for the subsession
- udpConn: UDP connection for receiving forwarded datagrams (required, not nil)
Returns a Datagram2Session ready for use without attempting to create a new SAM
session.
Example usage with PRIMARY session:
primary, err := sam.NewPrimarySession("main", keys, options)
udpConn, _ := net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 0})
sub, err := NewDatagram2SessionFromSubsession(sam, "sub1", keys, options, udpConn)
Example usage: session, err := NewDatagram2SessionFromSubsession(sam, "sub1",
keys, options, udpConn)
#### func (*Datagram2Session) Addr
@@ -434,12 +355,8 @@ func (s *Datagram2Session) Addr() i2pkeys.I2PAddr
Addr returns the I2P address of this datagram2 session. This address represents
the session's identity on the I2P network and can be used by other nodes to send
authenticated datagrams with replay protection to this session. The address is
derived from the session's cryptographic keys.
Example usage:
myAddr := session.Addr()
fmt.Println("My I2P address:", myAddr.Base32())
derived from the session's cryptographic keys. Example usage: myAddr :=
session.Addr(); fmt.Println("My I2P address:", myAddr.Base32())
#### func (*Datagram2Session) Close
@@ -449,11 +366,7 @@ func (s *Datagram2Session) Close() error
Close closes the datagram2 session and all associated resources. This method
safely terminates the session, closes the UDP listener and underlying
connection, and cleans up any background goroutines. It's safe to call multiple
times.
Example usage:
defer session.Close()
times. Example usage: defer session.Close()
#### func (*Datagram2Session) NewReader
@@ -463,24 +376,9 @@ func (s *Datagram2Session) NewReader() *Datagram2Reader
NewReader creates a Datagram2Reader for receiving authenticated datagrams with
replay protection. This method initializes a new reader with buffered channels
for asynchronous datagram reception. The reader must be started manually with
receiveLoop() for continuous operation.
All datagrams received through this reader are authenticated by the I2P router
with signature verification performed internally. DATAGRAM2 provides replay
protection, preventing replay attacks that are possible with legacy DATAGRAM
sessions.
Example usage:
reader := session.NewReader()
go reader.receiveLoop()
for {
datagram, err := reader.ReceiveDatagram()
if err != nil {
// Handle error
}
// Process datagram.Data from datagram.Source
}
receiveLoop() for continuous operation. Example usage: reader :=
session.NewReader(); go reader.receiveLoop(); datagram, err :=
reader.ReceiveDatagram()
#### func (*Datagram2Session) NewWriter
@@ -490,16 +388,9 @@ func (s *Datagram2Session) NewWriter() *Datagram2Writer
NewWriter creates a Datagram2Writer for sending authenticated datagrams with
replay protection. This method initializes a new writer with a default timeout
of 30 seconds for send operations. The timeout can be customized using the
SetTimeout method on the returned writer.
All datagrams sent through this writer are authenticated and provide replay
protection. Maximum datagram size is 31744 bytes total (including headers), with
11 KB recommended for best reliability across the I2P network.
Example usage:
writer := session.NewWriter().SetTimeout(60*time.Second)
err := writer.SendDatagram(data, destination)
SetTimeout method on the returned writer. Example usage: writer :=
session.NewWriter().SetTimeout(60*time.Second); err := writer.SendDatagram(data,
dest)
#### func (*Datagram2Session) PacketConn
@@ -525,18 +416,8 @@ func (s *Datagram2Session) ReceiveDatagram() (*Datagram2, error)
ReceiveDatagram receives a single authenticated datagram from the I2P network.
This method is a convenience wrapper that performs a direct single read
operation without starting a continuous receive loop. For continuous reception,
use NewReader() and manage the reader lifecycle manually.
All datagrams are authenticated by the I2P router with signature verification
performed internally. DATAGRAM2 provides replay protection.
Example usage:
datagram, err := session.ReceiveDatagram()
if err != nil {
// Handle error
}
// Process datagram.Data from datagram.Source
use NewReader() and manage the reader lifecycle manually. Example usage:
datagram, err := session.ReceiveDatagram()
#### func (*Datagram2Session) SendDatagram
@@ -546,15 +427,8 @@ func (s *Datagram2Session) SendDatagram(data []byte, dest i2pkeys.I2PAddr) error
SendDatagram sends an authenticated datagram with replay protection to the
specified destination. This is a convenience method that creates a temporary
writer and sends the datagram immediately. For multiple sends, it's more
efficient to create a writer once and reuse it.
Maximum datagram size is 31744 bytes total (including headers), with 11 KB
recommended for best reliability across the I2P network. The datagram is
authenticated and provides replay protection.
Example usage:
err := session.SendDatagram([]byte("hello"), destinationAddr)
efficient to create a writer once and reuse it. Example usage: err :=
session.SendDatagram([]byte("hello"), destinationAddr)
#### type Datagram2Writer
@@ -584,18 +458,10 @@ Example usage:
func (w *Datagram2Writer) SendDatagram(data []byte, dest i2pkeys.I2PAddr) error
```
SendDatagram sends an authenticated datagram with replay protection to the
specified I2P destination. This method uses the SAMv3 UDP approach: sending via
UDP socket to port 7655 with DATAGRAM2 format. The datagram is authenticated by
the I2P router and provides replay protection not available in legacy DATAGRAM
sessions.
Maximum datagram size is 31744 bytes total (including headers), with 11 KB
recommended for best reliability across the I2P network. It blocks until the
datagram is sent or an error occurs, respecting the configured timeout.
Example usage:
err := writer.SendDatagram([]byte("hello world"), destinationAddr)
specified I2P destination. It uses the SAMv3 UDP approach by sending to port
7655 with DATAGRAM2 format. Maximum datagram size is 31744 bytes (11 KB
recommended for reliability). Example usage: err :=
writer.SendDatagram([]byte("hello world"), destinationAddr)
#### func (*Datagram2Writer) SetTimeout
@@ -604,13 +470,8 @@ func (w *Datagram2Writer) SetTimeout(timeout time.Duration) *Datagram2Writer
```
SetTimeout sets the timeout for datagram2 write operations. This method
configures the maximum time to wait for authenticated datagram send operations
to complete. The timeout prevents indefinite blocking during network congestion
or connection issues. Returns the writer instance for method chaining
convenience.
Example usage:
writer.SetTimeout(30*time.Second).SendDatagram(data, destination)
to complete. Returns the writer instance for method chaining convenience.
Example usage: writer.SetTimeout(30*time.Second).SendDatagram(data, destination)
#### type SAM

View File

@@ -2,225 +2,34 @@
--
import "github.com/go-i2p/go-sam-go/datagram3"
Package datagram3 provides repliable but UNAUTHENTICATED datagram sessions for
I2P.
Package datagram3 provides repliable datagram sessions with hash-based source
identification for I2P.
# CRITICAL SECURITY WARNING
⚠️ DATAGRAM3 sources are NOT authenticated and can be spoofed by malicious
actors!
⚠️ Any attacker can claim to be any sender by providing a fake hash.
⚠️ Do NOT trust source identity without additional application-level
authentication.
⚠️ If you need authenticated sources, use DATAGRAM2 instead.
# Overview
DATAGRAM3 provides UDP-like messaging over I2P with hash-based source
identification. This format prioritizes low overhead over source verification,
making it suitable for applications that implement their own authentication
layer or where source identity is not security-critical.
DATAGRAM3 sessions provide repliable UDP-like messaging with hash-based source
identification instead of full destinations.
Key features:
- Repliable datagrams (can send replies to sender)
- Unauthenticated sources (hash-based, spoofable)
- Offline signature support
- Repliable (can send replies to sender)
- Hash-based source identification (32-byte hash)
- Requires NAMING LOOKUP for replies
- UDP-like messaging (unreliable, unordered)
- Maximum 31744 bytes (recommended 11 KB for reliability)
- Automatic hash-to-destination caching via NAMING LOOKUP
- Maximum 31744 bytes per datagram (11 KB recommended)
# Key Differences from DATAGRAM and DATAGRAM2
Session creation requires 2-5 minutes for I2P tunnel establishment. Use generous
timeouts and exponential backoff retry logic. Hash resolution uses automatic
caching to minimize NAMING LOOKUP overhead.
Feature | DATAGRAM | DATAGRAM2 | DATAGRAM3
---------------------|----------|-----------|----------
Authenticated | Yes | Yes | NO
Repliable | Yes | Yes | Yes
Replay Protection | No | Yes | No
Offline Signatures | No | Yes | Yes
Source Format | Full | Full | 32-byte hash
Source Verification | Yes | Yes | NO
Reply Overhead | Low | Low | Higher (NAMING LOOKUP)
Basic usage:
# Security Considerations
DATAGRAM3 is appropriate when:
- Application implements its own authentication layer
- Source identity is not security-critical
- High-volume messaging requiring low overhead
- Anonymous bulletin boards or chat systems
- Telemetry or metrics collection
DATAGRAM3 is NOT appropriate for:
- Financial transactions
- Identity-based access control
- Cryptographic protocols relying on source verification
- Any system where source spoofing has security implications
# I2P Timing Considerations
Session creation: 2-5 minutes for I2P tunnel establishment
Datagram delivery: Variable latency (network-dependent)
Hash resolution: Additional network round-trip for NAMING LOOKUP (cached after
first use)
Recommended: Use generous timeouts (5+ minutes for session creation) and retry
logic with exponential backoff. Distinguish between I2P network delays and
actual failures.
# Basic Usage
Create a session:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
sam, err := common.NewSAM("127.0.0.1:7656")
session, err := datagram3.NewDatagram3Session(sam, "my-session", keys, []string{"inbound.length=1"})
defer session.Close()
dg, err := session.NewReader().ReceiveDatagram()
if err := dg.ResolveSource(session); err != nil { log.Error(err) }
session.NewWriter().SendDatagram([]byte("reply"), dg.Source)
Receive datagrams with hash resolution:
reader := session.NewReader()
go reader.receiveLoop()
for {
dg, err := reader.ReceiveDatagram()
if err != nil {
log.Error(err)
continue
}
// SECURITY: dg.SourceHash is UNAUTHENTICATED!
log.Warn("Received from unverified hash:", hex.EncodeToString(dg.SourceHash))
// Resolve hash to full destination for reply (cached after first lookup)
if err := dg.ResolveSource(session); err != nil {
log.Error("Failed to resolve:", err)
continue
}
// Process data and reply (source still unverified!)
writer := session.NewWriter()
writer.SendDatagram([]byte("reply"), dg.Source)
}
Send datagrams:
writer := session.NewWriter().SetTimeout(30*time.Second)
err := writer.SendDatagram(data, destination)
# Hash Resolution for Replies
Received DATAGRAM3 messages contain a 44-byte base64 hash (32 bytes binary)
instead of the full sender destination. To reply, the hash must be converted to
a b32.i2p address and resolved via NAMING LOOKUP:
1. Receive 44-byte base64 hash from SAM bridge
2. Base64-decode to 32 bytes binary
3. Base32-encode to 52 characters
4. Append ".b32.i2p" suffix
5. Use resulting address for NAMING LOOKUP to get full destination
6. Cache full destination to avoid repeated lookups
7. Use full destination for SendDatagram()
The session maintains a HashResolver cache to minimize lookup overhead.
Applications replying to the same source repeatedly benefit from caching.
Example with manual hash handling:
// Get b32 address without full resolution (fast, no network I/O)
b32Addr := datagram.GetSourceB32()
log.Info("Received from (unverified):", b32Addr)
// Check if already cached (no network I/O)
if dest, ok := session.resolver.GetCached(datagram.SourceHash); ok {
writer.SendDatagram(reply, dest)
} else {
// Resolve with network I/O
if err := datagram.ResolveSource(session); err != nil {
log.Error(err)
}
}
# PRIMARY Session Usage
DATAGRAM3 supports SAMv3.3 PRIMARY sessions for sharing tunnels across multiple
subsessions:
primary, err := sam.NewPrimarySession("main", keys, options)
udpConn, _ := net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 0})
sub1, err := datagram3.NewDatagram3SessionFromSubsession(sam, "sub1", keys, options, udpConn)
Each subsession can use LISTEN_PORT to differentiate incoming datagrams.
# Implementation Status
DATAGRAM3 is a new format specified in early 2025. This is one of the first
implementations. Check SAM bridge documentation for I2P router support (Java I2P
0.9.x+, i2pd 2.x+).
Current implementation status:
- Core session management: ✅ Implemented
- UDP forwarding: ✅ Implemented
- Hash resolution and caching: ✅ Implemented
- Send/receive operations: ✅ Implemented
- PacketConn interface: ✅ Implemented
- PRIMARY session integration: ⏸️ Deferred (low priority)
# Attack Scenarios
Source Spoofing: Attacker claims to be legitimate sender by providing fake hash.
Applications trust unauthenticated source for security decisions. Mitigation:
Application-layer authentication required.
Cache Poisoning: Attacker floods with fake sources causing cache to grow
unbounded. Mitigation: Implement cache size limits and TTL (planned
enhancement).
Impersonation: Attacker replies pretending to be original sender. No
cryptographic verification of source identity. Mitigation: Use DATAGRAM2 for
authenticated sources.
# Performance Considerations
Hash resolution overhead: First reply to any source requires NAMING LOOKUP
(network I/O). Subsequent replies to same source use cached destination (fast).
Cache hit rate improves with repeated communication to same peers. Consider
pre-resolving known peers during initialization for critical paths.
Memory usage: Hash resolver cache grows unbounded. Monitor cache size with
CacheSize(). Consider implementing periodic cache clearing for long-running
applications. Future enhancement: LRU eviction policy and cache size limits.
# Protocol Compliance
This implementation follows the SAMv3 specification for DATAGRAM3 sessions:
- STYLE=DATAGRAM3 (not DATAGRAM or DATAGRAM2)
- UDP forwarding (PORT/HOST) is mandatory for SAMv3
- Supports SAM 3.2+ features (FROM_PORT/TO_PORT)
- Supports SAM 3.3+ features (SEND_TAGS, TAG_THRESHOLD, EXPIRES, SEND_LEASESET)
- Offline signature support via SIGNATURE_TYPE
- PRIMARY session subsession support
# See Also
Package datagram: Legacy DATAGRAM sessions (authenticated, repliable)
Package datagram2: DATAGRAM2 sessions (authenticated, repliable, replay
protection)
Package stream: TCP-like reliable connections
Package raw: Encrypted but unauthenticated datagrams (non-repliable)
Package primary: PRIMARY session management for multiple subsessions
See also: Package datagram, datagram2, stream, raw, primary.
## Usage
@@ -237,11 +46,6 @@ type Datagram3 struct {
Datagram3 represents an I2P datagram3 message with UNAUTHENTICATED source.
⚠️ CRITICAL SECURITY WARNING: SourceHash is NOT authenticated and can be
spoofed! ⚠️ Any malicious actor can claim to be any source by providing a fake
hash. ⚠️ Applications MUST implement their own authentication if source identity
matters. ⚠️ Use DATAGRAM2 if you need cryptographically authenticated sources.
This structure encapsulates the payload data along with the unauthenticated
source hash and optional resolved destination. The SourceHash is always present
(32 bytes), while Source is only populated after calling ResolveSource() to
@@ -277,9 +81,6 @@ resolution. This converts the 32-byte hash to a base32-encoded .b32.i2p address
string without performing NAMING LOOKUP. This is faster than full resolution and
sufficient for display, logging, or caching purposes.
⚠️ SECURITY WARNING: The returned address is still UNAUTHENTICATED! ⚠️ This
method does not add source verification.
Returns empty string if SourceHash is invalid (not 32 bytes).
Example usage:
@@ -297,10 +98,6 @@ This performs a NAMING LOOKUP to convert the 32-byte hash into a full
destination address. The operation is cached in the session's resolver to avoid
repeated lookups.
⚠️ SECURITY WARNING: Resolving the hash does NOT authenticate the source! ⚠️
Even with full destination, the source can still be spoofed. ⚠️ This method only
enables replies, it does NOT verify identity.
Process:
1. Check if already resolved (Source not nil)
@@ -330,10 +127,6 @@ type Datagram3Addr struct {
Datagram3Addr implements net.Addr interface for I2P datagram3 addresses.
⚠️ SECURITY WARNING: If constructed from received hash, this address is
UNAUTHENTICATED! ⚠️ Do not trust the address for security-critical operations
without additional verification.
This type provides standard Go networking address representation for I2P
destinations, allowing seamless integration with existing Go networking code
that expects net.Addr. The address can wrap either a full I2P destination or
@@ -361,8 +154,6 @@ String returns the string representation of the I2P address. This implements the
net.Addr interface. If a full address is available, returns base32
representation. If only hash is available, returns the b32.i2p derived address.
⚠️ SECURITY WARNING: Hash-derived addresses are UNAUTHENTICATED!
#### type Datagram3Conn
```go
@@ -373,10 +164,6 @@ type Datagram3Conn struct {
Datagram3Conn implements net.PacketConn interface for I2P datagram3
communication.
⚠️ SECURITY WARNING: All sources received via this connection are
UNAUTHENTICATED! ⚠️ Applications MUST implement their own authentication layer
if source identity matters.
This type provides compatibility with standard Go networking patterns by
wrapping datagram3 session functionality in a familiar PacketConn interface. It
manages internal readers and writers while providing standard connection
@@ -422,8 +209,6 @@ data into the provided byte slice and returns the number of bytes read. When
reading, it also updates the remote address of the connection for subsequent
Write calls.
⚠️ SECURITY WARNING: Remote address is UNAUTHENTICATED hash-based!
Note: This is not typical for datagrams which are connectionless, but provides
compatibility with the net.Conn interface.
@@ -432,16 +217,12 @@ compatibility with the net.Conn interface.
```go
func (c *Datagram3Conn) ReadFrom(p []byte) (n int, addr net.Addr, err error)
```
ReadFrom reads an UNAUTHENTICATED datagram from the connection.
⚠️ CRITICAL SECURITY WARNING: Source addresses are NOT authenticated! ⚠️ The
returned address contains an UNAUTHENTICATED hash-based source. ⚠️ Do not trust
source identity without additional verification.
ReadFrom reads a datagram from the connection.
This method implements the net.PacketConn interface. It starts the receive loop
if not already started and blocks until a datagram is received. The data is
copied to the provided buffer p, and the UNAUTHENTICATED source address is
returned as a Datagram3Addr.
copied to the provided buffer p, and the source address is returned as a
Datagram3Addr.
The source address contains the 32-byte hash (not full destination).
Applications must resolve the hash via ResolveSource() to reply.
@@ -453,10 +234,8 @@ func (c *Datagram3Conn) RemoteAddr() net.Addr
```
RemoteAddr returns the remote network address of the connection. This method
implements the net.Conn interface. For datagram3 connections, this returns the
UNAUTHENTICATED address of the last peer that sent data (set by Read), or nil if
no data has been received yet.
⚠️ SECURITY WARNING: Remote address is UNAUTHENTICATED!
address of the last peer that sent data (set by Read), or nil if no data has
been received yet.
#### func (*Datagram3Conn) SetDeadline
@@ -520,10 +299,6 @@ type Datagram3Reader struct {
Datagram3Reader handles incoming UNAUTHENTICATED datagram3 reception from I2P.
⚠️ SECURITY WARNING: All received datagrams have UNAUTHENTICATED sources! ⚠️ The
SourceHash field in received datagrams can be spoofed by attackers. ⚠️ Do not
trust source identity without additional verification.
The reader provides asynchronous datagram reception through buffered channels,
allowing applications to receive datagrams without blocking. It manages its own
goroutine for continuous message processing and provides thread-safe access to
@@ -569,16 +344,11 @@ Example usage:
```go
func (r *Datagram3Reader) ReceiveDatagram() (*Datagram3, error)
```
ReceiveDatagram receives a single UNAUTHENTICATED datagram from the I2P network.
⚠️ CRITICAL SECURITY WARNING: Sources are NOT authenticated and can be spoofed!
⚠️ Do not trust datagram.SourceHash without additional verification. ⚠️ Use
application-layer authentication if source identity matters.
ReceiveDatagram receives a single datagram from the I2P network.
This method blocks until a datagram is received or an error occurs, returning
the received datagram with its data and UNAUTHENTICATED hash-based source. It
handles concurrent access safely and provides proper error handling for network
issues.
the received datagram with its data and hash-based source. It handles concurrent
access safely and provides proper error handling for network issues.
Unlike DATAGRAM/DATAGRAM2, received datagrams contain only a 32-byte hash (not
full destination). Applications must call ResolveSource() to convert the hash to
@@ -590,9 +360,7 @@ Example usage:
if err != nil {
// Handle error
}
// SECURITY: datagram.SourceHash is UNAUTHENTICATED!
log.Warn("Received from unverified source:", hex.EncodeToString(datagram.SourceHash))
// Resolve hash for reply (expensive, cached)
log.Info("Received from source:", hex.EncodeToString(datagram.SourceHash))
if err := datagram.ResolveSource(session); err != nil {
log.Error(err)
}
@@ -607,11 +375,6 @@ type Datagram3Session struct {
Datagram3Session represents a repliable but UNAUTHENTICATED datagram3 session.
⚠️ CRITICAL SECURITY WARNING: Source addresses are NOT authenticated and can be
spoofed! ⚠️ Applications requiring source authentication MUST use DATAGRAM2
instead. ⚠️ Do NOT trust source identity without additional application-level
authentication.
DATAGRAM3 provides UDP-like messaging with hash-based source identification
instead of full authenticated destinations. This reduces overhead at the cost of
source verification. Received datagrams contain a 32-byte hash that requires
@@ -656,46 +419,14 @@ Example usage:
```go
func NewDatagram3Session(sam *common.SAM, id string, keys i2pkeys.I2PKeys, options []string) (*Datagram3Session, error)
```
NewDatagram3Session creates a new repliable but UNAUTHENTICATED datagram3
session.
⚠️ CRITICAL SECURITY WARNING: DATAGRAM3 sources are NOT authenticated and can be
spoofed! ⚠️ Do not trust source addresses without additional application-level
authentication. ⚠️ If you need authenticated sources, use DATAGRAM2 instead.
This function establishes a new DATAGRAM3 session with the provided SAM
connection, session ID, cryptographic keys, and configuration options. It
automatically creates a UDP listener for receiving forwarded datagrams (SAMv3
requirement) and configures the session with PORT/HOST parameters.
DATAGRAM3 provides repliable datagrams with hash-based source identification:
- Repliable: Can reply to sender (requires hash resolution via NAMING LOOKUP)
- Unauthenticated: Source is NOT verified (unlike DATAGRAM/DATAGRAM2)
- Hash-based source: 32-byte hash instead of full destination
- Lower overhead: No signature verification required
I2P Timing Considerations:
- Session creation can take 2-5 minutes for I2P tunnel establishment
- Use context.WithTimeout with generous timeouts (5+ minutes recommended)
- Hash resolution adds network round-trip for replies (cached after first lookup)
- Implement exponential backoff retry logic for connection attempts
- Distinguish between I2P timing delays and actual failures
Example usage:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
session, err := NewDatagram3Session(sam, "my-session", keys, []string{"inbound.length=1"})
reader := session.NewReader()
go reader.receiveLoop()
dg, err := reader.ReceiveDatagram()
// dg.SourceHash is UNAUTHENTICATED!
if err := dg.ResolveSource(session); err != nil {
log.Fatal(err)
}
session.NewWriter().SendDatagram(reply, dg.Source)
NewDatagram3Session creates a new datagram3 session with hash-based source
identification. It initializes the session with the provided SAM connection,
session ID, cryptographic keys, and configuration options. The session
automatically creates a UDP listener for receiving forwarded datagrams per SAMv3
requirements and initializes a hash resolver for source lookups. Note: DATAGRAM3
sources are not authenticated; use datagram2 if authentication is required.
Example usage: session, err := NewDatagram3Session(sam, "my-session", keys,
[]string{"inbound.length=1"})
#### func NewDatagram3SessionFromSubsession
@@ -707,38 +438,13 @@ that has already been registered with a PRIMARY session using SESSION ADD. This
constructor skips the session creation step since the subsession is already
registered with the SAM bridge.
⚠️ SECURITY WARNING: Subsession sources are UNAUTHENTICATED just like primary
session sources! ⚠️ Do not trust source addresses without additional
verification.
This function is specifically designed for use with SAMv3.3 PRIMARY sessions
where subsessions are created using SESSION ADD rather than SESSION CREATE
commands.
For PRIMARY datagram3 subsessions, UDP forwarding is mandatory (SAMv3
requirement). The UDP connection must be provided for proper datagram reception
via UDP forwarding.
requirement). The UDP connection must be provided for proper datagram reception.
Note: Sources are not authenticated; use NewDatagramSubSession if authentication
is required.
DATAGRAM3 subsessions share the same cryptographic keys and I2P tunnels as the
PRIMARY session, but can be differentiated using LISTEN_PORT for incoming
datagram routing.
Parameters:
- sam: SAM connection for data operations (separate from the primary session's control connection)
- id: The subsession ID that was already registered with SESSION ADD
- keys: The I2P keys from the primary session (shared across all subsessions)
- options: Configuration options for the subsession
- udpConn: UDP connection for receiving forwarded datagrams (required, not nil)
Returns a Datagram3Session ready for use without attempting to create a new SAM
session.
Example usage with PRIMARY session:
primary, err := sam.NewPrimarySession("main", keys, options)
udpConn, _ := net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 0})
sub, err := NewDatagram3SessionFromSubsession(sam, "sub1", keys, options, udpConn)
Example usage: sub, err := NewDatagram3SessionFromSubsession(sam, "sub1", keys,
options, udpConn)
#### func (*Datagram3Session) Addr
@@ -756,47 +462,20 @@ func (s *Datagram3Session) Close() error
```
Close terminates the datagram3 session and cleans up all resources. This method
ensures proper cleanup of the UDP connection and I2P tunnels. After calling
Close(), the session cannot be reused.
Example usage:
defer session.Close()
Close(), the session cannot be reused. Example usage: defer session.Close()
#### func (*Datagram3Session) NewReader
```go
func (s *Datagram3Session) NewReader() *Datagram3Reader
```
NewReader creates a Datagram3Reader for receiving UNAUTHENTICATED datagrams.
⚠️ SECURITY WARNING: All datagrams received have UNAUTHENTICATED sources! ⚠️ Do
not trust source identity without additional verification.
This method initializes a new reader with buffered channels for asynchronous
datagram reception. The reader must be started manually with receiveLoop() for
continuous operation.
Unlike DATAGRAM/DATAGRAM2, received datagrams contain 32-byte hashes rather than
full destinations. Applications must call ResolveSource() on received datagrams
to obtain the full destination for replies. The session's resolver cache
minimizes lookup overhead.
Example usage:
reader := session.NewReader()
go reader.receiveLoop()
for {
datagram, err := reader.ReceiveDatagram()
if err != nil {
// Handle error
}
// SECURITY: datagram.SourceHash is UNAUTHENTICATED!
// Verify using application-layer authentication before trusting
if err := datagram.ResolveSource(session); err != nil {
// Handle resolution error
}
// Process datagram.Data
}
NewReader creates a Datagram3Reader for receiving datagrams with hash-based
sources. This method initializes a new reader with buffered channels for
asynchronous datagram reception. The reader must be started manually with
receiveLoop() for continuous operation. Received datagrams contain 32-byte
hashes; call ResolveSource() to obtain full destinations for replies. Example
usage: reader := session.NewReader(); go reader.receiveLoop(); datagram, err :=
reader.ReceiveDatagram()
#### func (*Datagram3Session) NewWriter
@@ -806,19 +485,9 @@ func (s *Datagram3Session) NewWriter() *Datagram3Writer
NewWriter creates a Datagram3Writer for sending datagrams to I2P destinations.
This method initializes a new writer with a default timeout of 30 seconds for
send operations. The timeout can be customized using the SetTimeout method on
the returned writer.
Destinations can be specified as full base64 destinations, hostnames (.i2p), or
b32 addresses. This includes b32 addresses derived from received DATAGRAM3
hashes after resolution.
Maximum datagram size is 31744 bytes total (including headers), with 11 KB
recommended for best reliability across the I2P network.
Example usage:
writer := session.NewWriter().SetTimeout(60*time.Second)
err := writer.SendDatagram(data, destination)
the returned writer. Example usage: writer :=
session.NewWriter().SetTimeout(60*time.Second); err := writer.SendDatagram(data,
dest)
#### func (*Datagram3Session) PacketConn
@@ -830,9 +499,6 @@ method provides compatibility with standard Go networking code by wrapping the
datagram3 session in a PacketConn interface. The returned connection manages its
own reader and writer and implements all standard net.PacketConn methods.
⚠️ SECURITY WARNING: All sources are UNAUTHENTICATED! ⚠️ Do not trust addresses
received via ReadFrom without verification.
The connection is automatically cleaned up by a finalizer if Close() is not
called, but explicit Close() calls are strongly recommended to prevent resource
leaks.
@@ -842,9 +508,8 @@ Example usage:
conn := session.PacketConn()
defer conn.Close()
// Receive with UNAUTHENTICATED source
// Receive source
n, addr, err := conn.ReadFrom(buffer)
// addr is UNAUTHENTICATED!
// Send reply
n, err = conn.WriteTo(reply, addr)
@@ -882,9 +547,6 @@ This automatically resolves the source hash if not already resolved, then sends
the reply. The source hash is resolved via NAMING LOOKUP and cached to avoid
repeated lookups.
⚠️ SECURITY WARNING: Even after resolution, the source is still UNAUTHENTICATED!
⚠️ Do not trust the reply destination without additional verification.
Example usage:
// Receive datagram

1210
datagram3/DOC.md.backup Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,11 @@
// Package datagram3 provides repliable datagram sessions with hash-based source identification for I2P.
//
// DATAGRAM3 sessions provide repliable UDP-like messaging with hash-based source identification
// instead of full destinations. Sources are not cryptographically authenticated; applications
// requiring authenticated sources should use datagram2 instead.
// instead of full destinations.
//
// Key features:
// - Repliable (can send replies to sender)
// - Hash-based source identification (32-byte hash)
// - No source authentication (spoofable)
// - Requires NAMING LOOKUP for replies
// - UDP-like messaging (unreliable, unordered)
// - Maximum 31744 bytes per datagram (11 KB recommended)
@@ -25,6 +23,5 @@
// if err := dg.ResolveSource(session); err != nil { log.Error(err) }
// session.NewWriter().SendDatagram([]byte("reply"), dg.Source)
//
// See also: Package datagram (legacy, authenticated), datagram2 (authenticated with replay
// protection), stream (TCP-like), raw (non-repliable), primary (multi-session management).
// See also: Package datagram, datagram2, stream, raw, primary.
package datagram3

View File

@@ -23,7 +23,6 @@ import (
// // Handle error
// }
// log.Info("Received from source:", hex.EncodeToString(datagram.SourceHash))
// // Resolve hash for reply (expensive, cached)
// if err := datagram.ResolveSource(session); err != nil {
// log.Error(err)
// }

View File

@@ -10,15 +10,11 @@ import (
"github.com/samber/oops"
)
// ReadFrom reads an UNAUTHENTICATED datagram from the connection.
//
// ⚠️ CRITICAL SECURITY WARNING: Source addresses are NOT authenticated!
// ⚠️ The returned address contains an UNAUTHENTICATED hash-based source.
// ⚠️ Do not trust source identity without additional verification.
// ReadFrom reads a datagram from the connection.
//
// This method implements the net.PacketConn interface. It starts the receive loop if not
// already started and blocks until a datagram is received. The data is copied to the provided
// buffer p, and the UNAUTHENTICATED source address is returned as a Datagram3Addr.
// buffer p, and the source address is returned as a Datagram3Addr.
//
// The source address contains the 32-byte hash (not full destination). Applications must
// resolve the hash via ResolveSource() to reply.
@@ -41,11 +37,11 @@ func (c *Datagram3Conn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
// Copy data to the provided buffer
n = copy(p, datagram.Data)
// Create address with UNAUTHENTICATED hash
// Create address with hash
// Applications can check addr.(*Datagram3Addr).hash for the hash
addr = &Datagram3Addr{
addr: datagram.Source, // May be empty if not resolved
hash: datagram.SourceHash, // 32-byte UNAUTHENTICATED hash
hash: datagram.SourceHash, // 32-byte hash
}
return n, addr, nil
@@ -189,8 +185,6 @@ func (c *Datagram3Conn) SetWriteDeadline(t time.Time) error {
// When reading, it also updates the remote address of the connection for subsequent
// Write calls.
//
// ⚠️ SECURITY WARNING: Remote address is UNAUTHENTICATED hash-based!
//
// Note: This is not typical for datagrams which are connectionless,
// but provides compatibility with the net.Conn interface.
func (c *Datagram3Conn) Read(b []byte) (n int, err error) {
@@ -210,10 +204,8 @@ func (c *Datagram3Conn) Read(b []byte) (n int, err error) {
// RemoteAddr returns the remote network address of the connection.
// This method implements the net.Conn interface. For datagram3 connections,
// this returns the UNAUTHENTICATED address of the last peer that sent data (set by Read),
// this returns the address of the last peer that sent data (set by Read),
// or nil if no data has been received yet.
//
// ⚠️ SECURITY WARNING: Remote address is UNAUTHENTICATED!
func (c *Datagram3Conn) RemoteAddr() net.Addr {
if c.remoteAddr != nil {
return &Datagram3Addr{addr: *c.remoteAddr}
@@ -266,9 +258,6 @@ func (c *Datagram3Conn) clearCleanup() {
// the datagram3 session in a PacketConn interface. The returned connection manages
// its own reader and writer and implements all standard net.PacketConn methods.
//
// ⚠️ SECURITY WARNING: All sources are UNAUTHENTICATED!
// ⚠️ Do not trust addresses received via ReadFrom without verification.
//
// The connection is automatically cleaned up by a finalizer if Close() is not called,
// but explicit Close() calls are strongly recommended to prevent resource leaks.
//
@@ -277,9 +266,8 @@ func (c *Datagram3Conn) clearCleanup() {
// conn := session.PacketConn()
// defer conn.Close()
//
// // Receive with UNAUTHENTICATED source
// // Receive source
// n, addr, err := conn.ReadFrom(buffer)
// // addr is UNAUTHENTICATED!
//
// // Send reply
// n, err = conn.WriteTo(reply, addr)

View File

@@ -11,26 +11,16 @@ import (
// readDatagramFromUDP reads a forwarded datagram3 message from the UDP connection.
//
// ⚠️ CRITICAL SECURITY WARNING: Source is a 44-byte base64 hash, NOT authenticated!
// ⚠️ The hash can be spoofed by malicious actors - do NOT trust without verification.
// ⚠️ This is fundamentally different from DATAGRAM/DATAGRAM2 authenticated sources.
//
// This is used for receiving datagrams where the SAM bridge forwards messages via UDP.
// DATAGRAM3 uses hash-based source identification instead of full destinations.
//
// Format per SAMv3.md:
//
// Line 1: $hash (44-byte base64 hash, UNAUTHENTICATED!)
// Line 1: $hash (44-byte base64 hash)
// [FROM_PORT=nnn] [TO_PORT=nnn] (SAMv3.2+, may be on one or two lines)
// Then: \n (empty line separator)
// Remaining: $datagram_payload (raw data)
//
// CRITICAL DIFFERENCE from DATAGRAM/DATAGRAM2:
// - Source is 44-byte base64 hash (32 bytes binary)
// - NOT a full destination (516+ chars)
// - Hash is UNAUTHENTICATED and spoofable
// - Must use NAMING LOOKUP to resolve hash for replies
//
// The hash decodes to 32 bytes binary. To reply:
// 1. Base32-encode hash to 52 characters
// 2. Append ".b32.i2p" suffix
@@ -57,14 +47,14 @@ func (s *Datagram3Session) readDatagramFromUDP(udpConn *net.UDPConn) (*Datagram3
return nil, oops.Errorf("invalid UDP datagram3 format: no newline found")
}
// Line 1: Source hash (44-byte base64, UNAUTHENTICATED!) followed by optional FROM_PORT=nnn TO_PORT=nnn
// Line 1: Source hash (44-byte base64) followed by optional FROM_PORT=nnn TO_PORT=nnn
headerLine := strings.TrimSpace(response[:firstNewline])
if headerLine == "" {
return nil, oops.Errorf("empty header line in UDP datagram3")
}
// Parse the header line to extract the UNAUTHENTICATED source hash
// Parse the header line to extract the source hash
// Format: "$hash_base64 FROM_PORT=nnn TO_PORT=nnn"
// We need to split on space and take the first part as the hash
parts := strings.Fields(headerLine)
@@ -72,10 +62,10 @@ func (s *Datagram3Session) readDatagramFromUDP(udpConn *net.UDPConn) (*Datagram3
return nil, oops.Errorf("empty header line in UDP datagram3")
}
hashBase64 := parts[0] // First field is the UNAUTHENTICATED source hash
hashBase64 := parts[0] // First field is the source hash
// Remaining parts are FROM_PORT and TO_PORT which we ignore for now
// CRITICAL VALIDATION: Hash MUST be exactly 44 bytes base64 (32 bytes binary)
// Hash MUST be exactly 44 bytes base64 (32 bytes binary)
if len(hashBase64) != 44 {
return nil, oops.Errorf("invalid hash length: %d (expected 44 base64 chars)", len(hashBase64))
}
@@ -106,24 +96,19 @@ func (s *Datagram3Session) readDatagramFromUDP(udpConn *net.UDPConn) (*Datagram3
return s.createDatagram(hashBytes, data)
}
// createDatagram constructs the final Datagram3 from parsed UNAUTHENTICATED hash and data.
//
// ⚠️ SECURITY WARNING: The hash is NOT authenticated and can be spoofed!
// ⚠️ Do NOT trust the source without additional verification.
// createDatagram constructs the final Datagram3 from parsed hash and data.
//
// The datagram is created with:
// - Data: Raw payload bytes
// - SourceHash: 32-byte UNAUTHENTICATED hash (spoofable!)
// - SourceHash: 32-byte hash
// - Source: Empty (not resolved, requires NAMING LOOKUP for replies)
// - Local: This session's I2P address
//
// Applications must call ResolveSource() to convert hash to full destination for replies.
func (s *Datagram3Session) createDatagram(hashBytes []byte, data string) (*Datagram3, error) {
// Create datagram with UNAUTHENTICATED hash
// Create datagram with hash
// Source is empty (not resolved) - applications resolve on-demand for replies
datagram := &Datagram3{
Data: []byte(data),
SourceHash: hashBytes, // 32-byte UNAUTHENTICATED hash (spoofable!)
SourceHash: hashBytes, // 32-byte hash
Source: "", // Not resolved (empty = requires ResolveSource())
Local: s.Addr(),
}
@@ -132,7 +117,7 @@ func (s *Datagram3Session) createDatagram(hashBytes []byte, data string) (*Datag
"data_len": len(datagram.Data),
"hash_len": len(datagram.SourceHash),
"source_set": datagram.Source != "",
}).Debug("Created datagram3 with UNAUTHENTICATED hash source")
}).Debug("Created datagram3 with hash source")
return datagram, nil
}

View File

@@ -410,7 +410,7 @@ func TestDatagram3RoundTrip(t *testing.T) {
// Test sending reply
t.Logf("Bob sending reply to Alice...")
writerB := sessionB.NewWriter()
replyMessage := []byte("Reply from Bob! Source verification is YOUR responsibility!")
replyMessage := []byte("Reply from Bob!")
err = writerB.ReplyToDatagram(replyMessage, dg)
if err != nil {
t.Errorf("Failed to send reply: %v", err)

View File

@@ -141,6 +141,6 @@ func (w *Datagram3Writer) ReplyToDatagram(data []byte, original *Datagram3) erro
}
}
// Send to resolved source (still UNAUTHENTICATED!)
// Send to resolved source
return w.SendDatagram(data, original.Source)
}

View File

@@ -2,6 +2,36 @@
--
import "github.com/go-i2p/go-sam-go/primary"
Package primary provides PRIMARY session management for sharing I2P tunnels
across multiple subsessions.
PRIMARY sessions allow multiple subsessions (stream, datagram, datagram2,
datagram3, raw) to share a single set of I2P tunnels, reducing resource usage
and tunnel setup overhead. Each subsession operates independently while using
the master session's tunnels.
Key features:
- Single tunnel setup for multiple subsessions
- Mixed subsession types (stream, datagram, raw)
- Independent subsession lifecycle management
- Reduced resource usage and setup time
- SAMv3.3 PRIMARY protocol compliance
Primary session creation requires 2-5 minutes for I2P tunnel establishment.
Subsessions attach quickly since tunnels are already established. Use generous
timeouts for initial PRIMARY session creation.
Basic usage:
sam, err := common.NewSAM("127.0.0.1:7656")
primary, err := primary.NewPrimarySession(sam, "master", keys, []string{"inbound.length=1"})
defer primary.Close()
streamSub, err := primary.NewStreamSubsession("stream-1")
datagramSub, err := primary.NewDatagramSubsession("dgram-1")
See also: Package stream, datagram, datagram2, datagram3, raw for individual
session types.
## Usage
@@ -122,38 +152,20 @@ type PrimarySession struct {
}
```
PrimarySession provides master session capabilities for managing multiple
sub-sessions of different types (stream, datagram, raw) within a single I2P
session context. It enables complex applications with multiple communication
patterns while sharing the same I2P identity and tunnel infrastructure for
enhanced efficiency and anonymity.
The primary session manages the lifecycle of all sub-sessions, ensures proper
cleanup cascading when the primary session is closed, and provides thread-safe
operations for creating, managing, and terminating sub-sessions across different
protocols.
PrimarySession manages multiple sub-sessions sharing the same I2P identity and
tunnels.
#### func NewPrimarySession
```go
func NewPrimarySession(sam *common.SAM, id string, keys i2pkeys.I2PKeys, options []string) (*PrimarySession, error)
```
NewPrimarySession creates a new primary session with the provided SAM
connection, session ID, cryptographic keys, and configuration options. The
primary session acts as a master container that can create and manage multiple
sub-sessions of different types while sharing the same I2P identity and tunnel
infrastructure.
The session uses PRIMARY session type in the SAM protocol, which allows multiple
sub-sessions to be created using the same underlying I2P destination and keys.
This provides better resource efficiency and maintains consistent identity
across different communication patterns within the same application.
Example usage:
session, err := NewPrimarySession(sam, "my-primary", keys, []string{"inbound.length=2"})
streamSub, err := session.NewStreamSubSession("stream-1", streamOptions)
datagramSub, err := session.NewDatagramSubSession("datagram-1", datagramOptions)
NewPrimarySession creates a new primary session for managing multiple
sub-sessions. It initializes the session with the provided SAM connection,
session ID, cryptographic keys, and configuration options. The primary session
allows creating multiple sub-sessions of different types (stream, datagram, raw)
while sharing the same I2P identity and tunnels. Example usage: session, err :=
NewPrimarySession(sam, "my-primary", keys, []string{"inbound.length=2"})
#### func NewPrimarySessionWithSignature
@@ -161,20 +173,10 @@ Example usage:
func NewPrimarySessionWithSignature(sam *common.SAM, id string, keys i2pkeys.I2PKeys, options []string, sigType string) (*PrimarySession, error)
```
NewPrimarySessionWithSignature creates a new primary session with the specified
signature type. This is a package-level function that provides direct access to
signature-aware session creation without requiring wrapper types. It delegates
to the common package for session creation while maintaining the same primary
session functionality and sub-session management capabilities.
The signature type allows specifying custom cryptographic parameters for
enhanced security or compatibility with specific I2P network configurations.
Different signature types provide various security levels, performance
characteristics, and compatibility options.
Example usage:
session, err := NewPrimarySessionWithSignature(sam, "secure-primary", keys, options, "EdDSA_SHA512_Ed25519")
streamSub, err := session.NewStreamSubSession("stream-1", streamOptions)
signature type. This method allows specifying custom cryptographic parameters
for enhanced security or compatibility with specific I2P network configurations.
Example usage: session, err := NewPrimarySessionWithSignature(sam,
"secure-primary", keys, options, "EdDSA_SHA512_Ed25519")
#### func (*PrimarySession) Addr
@@ -354,17 +356,8 @@ NewStreamSubSession creates a new stream sub-session within this primary
session. The sub-session shares the primary session's I2P identity and tunnel
infrastructure while providing full StreamSession functionality for TCP-like
reliable connections. Each sub-session must have a unique identifier within the
primary session scope.
This implementation uses the SAMv3.3 SESSION ADD protocol to properly register
the subsession with the primary session's SAM connection, ensuring compliance
with the I2P SAM protocol specification for PRIMARY session management.
Example usage:
streamSub, err := primary.NewStreamSubSession("tcp-handler", []string{"FROM_PORT=8080"})
listener, err := streamSub.Listen()
conn, err := streamSub.Dial("destination.b32.i2p")
primary session scope. Example usage: streamSub, err :=
primary.NewStreamSubSession("tcp-handler", []string{"FROM_PORT=8080"})
#### func (*PrimarySession) NewUniqueStreamSubSession

View File

@@ -2,6 +2,36 @@
--
import "github.com/go-i2p/go-sam-go/raw"
Package raw provides encrypted but unauthenticated, non-repliable datagram
sessions for I2P.
RAW sessions send encrypted datagrams without source authentication or reply
capability. Recipients cannot verify sender identity or send replies. Suitable
for one-way broadcast scenarios (logging, metrics, announcements) where reply
capability is not needed.
Key features:
- Encrypted transmission (confidentiality)
- No source authentication (spoofable)
- Non-repliable (recipient cannot reply)
- UDP-like messaging (unreliable, unordered)
- Maximum 31744 bytes per datagram (11 KB recommended)
Session creation requires 2-5 minutes for I2P tunnel establishment. Use generous
timeouts and exponential backoff retry logic.
Basic usage:
sam, err := common.NewSAM("127.0.0.1:7656")
session, err := raw.NewRawSession(sam, "my-session", keys, []string{"inbound.length=1"})
defer session.Close()
conn := session.PacketConn()
n, err := conn.WriteTo(data, destination)
See also: Package datagram (authenticated, repliable), datagram2 (with replay
protection), datagram3 (hash-based sources), stream (TCP-like), primary
(multi-session management).
## Usage

View File

@@ -2,6 +2,37 @@
--
import "github.com/go-i2p/go-sam-go/stream"
Package stream provides TCP-like reliable connections for I2P using SAMv3 STREAM
sessions.
STREAM sessions provide ordered, reliable, bidirectional byte streams over I2P
tunnels, implementing standard net.Conn and net.Listener interfaces. Ideal for
applications requiring TCP-like semantics (HTTP servers, file transfers,
persistent connections).
Key features:
- Ordered, reliable delivery
- Bidirectional communication
- Standard net.Conn/net.Listener interfaces
- Automatic connection management
- Compatible with io.Reader/io.Writer
Session creation requires 2-5 minutes for I2P tunnel establishment. Individual
connections (Accept/Dial) require additional time for circuit building. Use
generous timeouts and exponential backoff retry logic.
Basic usage:
sam, err := common.NewSAM("127.0.0.1:7656")
session, err := stream.NewStreamSession(sam, "my-session", keys, []string{"inbound.length=1"})
defer session.Close()
listener, err := session.Listen()
conn, err := listener.Accept()
defer conn.Close()
See also: Package datagram (UDP-like messaging), raw (unrepliable datagrams),
primary (multi-session management).
## Usage