eliminate a bunch of broken crap and start over from common

This commit is contained in:
eyedeekay
2025-05-27 18:25:13 -04:00
parent 479f2d20cc
commit d3f085b2c8
45 changed files with 190 additions and 3131 deletions

View File

@ -1,11 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
import (
"github.com/go-i2p/go-sam-go/stream"
)
// Implements net.Conn
type SAMConn struct {
*stream.StreamConn
}

View File

@ -4,7 +4,6 @@ import (
"bufio"
"bytes"
"io"
"net"
"os"
"strings"
@ -131,99 +130,6 @@ func (sam *SAM) Lookup(name string) (i2pkeys.I2PAddr, error) {
return sam.SAMResolver.Resolve(name)
}
// Creates a new session with the style of either "STREAM", "DATAGRAM" or "RAW",
// for a new I2P tunnel with name id, using the cypher keys specified, with the
// I2CP/streaminglib-options as specified. Extra arguments can be specified by
// setting extra to something else than []string{}.
// This sam3 instance is now a session
func (sam *SAM) NewGenericSession(style, id string, keys i2pkeys.I2PKeys, extras []string) (net.Conn, error) {
log.WithFields(logrus.Fields{"style": style, "id": id}).Debug("Creating new generic session")
return sam.NewGenericSessionWithSignature(style, id, keys, SIG_EdDSA_SHA512_Ed25519, extras)
}
func (sam *SAM) NewGenericSessionWithSignature(style, id string, keys i2pkeys.I2PKeys, sigType string, extras []string) (net.Conn, error) {
log.WithFields(logrus.Fields{"style": style, "id": id, "sigType": sigType}).Debug("Creating new generic session with signature")
return sam.NewGenericSessionWithSignatureAndPorts(style, id, "0", "0", keys, sigType, extras)
}
// Creates a new session with the style of either "STREAM", "DATAGRAM" or "RAW",
// for a new I2P tunnel with name id, using the cypher keys specified, with the
// I2CP/streaminglib-options as specified. Extra arguments can be specified by
// setting extra to something else than []string{}.
// This sam3 instance is now a session
func (sam *SAM) NewGenericSessionWithSignatureAndPorts(style, id, from, to string, keys i2pkeys.I2PKeys, sigType string, extras []string) (net.Conn, error) {
log.WithFields(logrus.Fields{"style": style, "id": id, "from": from, "to": to, "sigType": sigType}).Debug("Creating new generic session with signature and ports")
optStr := sam.SamOptionsString()
extraStr := strings.Join(extras, " ")
conn := sam.Conn
fp := ""
tp := ""
if from != "0" {
fp = " FROM_PORT=" + from
}
if to != "0" {
tp = " TO_PORT=" + to
}
scmsg := []byte("SESSION CREATE STYLE=" + style + fp + tp + " ID=" + id + " DESTINATION=" + keys.String() + " " + optStr + extraStr + "\n")
log.WithField("message", string(scmsg)).Debug("Sending SESSION CREATE message")
for m, i := 0, 0; m != len(scmsg); i++ {
if i == 15 {
log.Error("Failed to write SESSION CREATE message after 15 attempts")
conn.Close()
return nil, oops.Errorf("writing to SAM failed")
}
n, err := conn.Write(scmsg[m:])
if err != nil {
log.WithError(err).Error("Failed to write to SAM connection")
conn.Close()
return nil, oops.Errorf("writing to connection failed: %w", err)
}
m += n
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
log.WithError(err).Error("Failed to read SAM response")
conn.Close()
return nil, oops.Errorf("reading from connection failed: %w", err)
}
text := string(buf[:n])
log.WithField("response", text).Debug("Received SAM response")
if strings.HasPrefix(text, SESSION_OK) {
if keys.String() != text[len(SESSION_OK):len(text)-1] {
log.Error("SAM created a tunnel with different keys than requested")
conn.Close()
return nil, oops.Errorf("SAMv3 created a tunnel with keys other than the ones we asked it for")
}
log.Debug("Successfully created new session")
return conn, nil //&StreamSession{id, conn, keys, nil, sync.RWMutex{}, nil}, nil
} else if text == SESSION_DUPLICATE_ID {
log.Error("Duplicate tunnel name")
conn.Close()
return nil, oops.Errorf("Duplicate tunnel name")
} else if text == SESSION_DUPLICATE_DEST {
log.Error("Duplicate destination")
conn.Close()
return nil, oops.Errorf("Duplicate destination")
} else if text == SESSION_INVALID_KEY {
log.Error("Invalid key for SAM session")
conn.Close()
return nil, oops.Errorf("Invalid key - SAM session")
} else if strings.HasPrefix(text, SESSION_I2P_ERROR) {
log.WithField("error", text[len(SESSION_I2P_ERROR):]).Error("I2P error")
conn.Close()
return nil, oops.Errorf("I2P error " + text[len(SESSION_I2P_ERROR):])
} else {
log.WithField("reply", text).Error("Unable to parse SAMv3 reply")
conn.Close()
return nil, oops.Errorf("Unable to parse SAMv3 reply: " + text)
}
}
// close this sam session
func (sam *SAM) Close() error {
if sam.Conn != nil {

View File

@ -67,6 +67,7 @@ func NewSAM(address string) (*SAM, error) {
if err = sendHelloAndValidate(conn, s); err != nil {
logger.WithError(err).Error("Failed to send hello and validate SAM connection")
conn.Close()
return nil, err
}
@ -75,6 +76,7 @@ func NewSAM(address string) (*SAM, error) {
resolver, err := NewSAMResolver(s)
if err != nil {
logger.WithError(err).Error("Failed to create SAM resolver")
conn.Close()
return nil, oops.Errorf("failed to create SAM resolver: %w", err)
}
s.SAMResolver = *resolver

107
common/session.go Normal file
View File

@ -0,0 +1,107 @@
package common
import (
"strings"
"github.com/go-i2p/i2pkeys"
"github.com/samber/oops"
"github.com/sirupsen/logrus"
)
// Creates a new session with the style of either "STREAM", "DATAGRAM" or "RAW",
// for a new I2P tunnel with name id, using the cypher keys specified, with the
// I2CP/streaminglib-options as specified. Extra arguments can be specified by
// setting extra to something else than []string{}.
// This sam3 instance is now a session
func (sam SAM) NewGenericSession(style, id string, keys i2pkeys.I2PKeys, extras []string) (Session, error) {
log.WithFields(logrus.Fields{"style": style, "id": id}).Debug("Creating new generic session")
return sam.NewGenericSessionWithSignature(style, id, keys, SIG_EdDSA_SHA512_Ed25519, extras)
}
func (sam SAM) NewGenericSessionWithSignature(style, id string, keys i2pkeys.I2PKeys, sigType string, extras []string) (Session, error) {
log.WithFields(logrus.Fields{"style": style, "id": id, "sigType": sigType}).Debug("Creating new generic session with signature")
return sam.NewGenericSessionWithSignatureAndPorts(style, id, "0", "0", keys, sigType, extras)
}
// Creates a new session with the style of either "STREAM", "DATAGRAM" or "RAW",
// for a new I2P tunnel with name id, using the cypher keys specified, with the
// I2CP/streaminglib-options as specified. Extra arguments can be specified by
// setting extra to something else than []string{}.
// This sam3 instance is now a session
func (sam SAM) NewGenericSessionWithSignatureAndPorts(style, id, from, to string, keys i2pkeys.I2PKeys, sigType string, extras []string) (Session, error) {
log.WithFields(logrus.Fields{"style": style, "id": id, "from": from, "to": to, "sigType": sigType}).Debug("Creating new generic session with signature and ports")
optStr := sam.SamOptionsString()
extraStr := strings.Join(extras, " ")
conn := sam.Conn
fp := ""
tp := ""
if from != "0" {
fp = " FROM_PORT=" + from
}
if to != "0" {
tp = " TO_PORT=" + to
}
scmsg := []byte("SESSION CREATE STYLE=" + style + fp + tp + " ID=" + id + " DESTINATION=" + keys.String() + " " + optStr + extraStr + "\n")
log.WithField("message", string(scmsg)).Debug("Sending SESSION CREATE message")
n, err := conn.Write(scmsg)
if err != nil {
log.WithError(err).Error("Failed to write to SAM connection")
conn.Close()
return nil, oops.Errorf("writing to connection failed: %w", err)
}
if n != len(scmsg) {
log.WithFields(logrus.Fields{
"written": n,
"total": len(scmsg),
}).Error("Incomplete write to SAM connection")
conn.Close()
return nil, oops.Errorf("incomplete write to connection")
}
buf := make([]byte, 4096)
n, err = conn.Read(buf)
if err != nil {
log.WithError(err).Error("Failed to read SAM response")
conn.Close()
return nil, oops.Errorf("reading from connection failed: %w", err)
}
text := string(buf[:n])
log.WithField("response", text).Debug("Received SAM response")
if strings.HasPrefix(text, SESSION_OK) {
if keys.String() != text[len(SESSION_OK):len(text)-1] {
log.Error("SAM created a tunnel with different keys than requested")
conn.Close()
return nil, oops.Errorf("SAMv3 created a tunnel with keys other than the ones we asked it for")
}
log.Debug("Successfully created new session")
return &BaseSession{
id: id,
conn: conn,
keys: keys,
SAM: sam,
}, nil
} else if text == SESSION_DUPLICATE_ID {
log.Error("Duplicate tunnel name")
conn.Close()
return nil, oops.Errorf("Duplicate tunnel name")
} else if text == SESSION_DUPLICATE_DEST {
log.Error("Duplicate destination")
conn.Close()
return nil, oops.Errorf("Duplicate destination")
} else if text == SESSION_INVALID_KEY {
log.Error("Invalid key for SAM session")
conn.Close()
return nil, oops.Errorf("Invalid key - SAM session")
} else if strings.HasPrefix(text, SESSION_I2P_ERROR) {
log.WithField("error", text[len(SESSION_I2P_ERROR):]).Error("I2P error")
conn.Close()
return nil, oops.Errorf("I2P error " + text[len(SESSION_I2P_ERROR):])
} else {
log.WithField("reply", text).Error("Unable to parse SAMv3 reply")
conn.Close()
return nil, oops.Errorf("Unable to parse SAMv3 reply: " + text)
}
}

View File

@ -87,3 +87,46 @@ func (opts Options) AsList() (ls []string) {
}
return
}
type Session interface {
net.Conn
ID() string
Keys() i2pkeys.I2PKeys
Close() error
// Add other session methods as needed
}
type BaseSession struct {
id string
conn net.Conn
keys i2pkeys.I2PKeys
SAM SAM
}
func (bs *BaseSession) ID() string { return bs.id }
func (bs *BaseSession) Keys() i2pkeys.I2PKeys { return bs.keys }
func (bs *BaseSession) Read(b []byte) (int, error) { return bs.conn.Read(b) }
func (bs *BaseSession) Write(b []byte) (int, error) { return bs.conn.Write(b) }
func (bs *BaseSession) Close() error { return bs.conn.Close() }
func (bs *BaseSession) LocalAddr() net.Addr {
return bs.conn.LocalAddr()
}
func (bs *BaseSession) RemoteAddr() net.Addr {
return bs.conn.RemoteAddr()
}
func (bs *BaseSession) SetDeadline(t time.Time) error {
return bs.conn.SetDeadline(t)
}
func (bs *BaseSession) SetReadDeadline(t time.Time) error {
return bs.conn.SetReadDeadline(t)
}
func (bs *BaseSession) SetWriteDeadline(t time.Time) error {
return bs.conn.SetWriteDeadline(t)
}
func (bs *BaseSession) From() string {
return bs.SAM.Fromport
}
func (bs *BaseSession) To() string {
return bs.SAM.Toport
}

View File

@ -1,63 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
import (
"fmt"
"github.com/go-i2p/go-sam-go/common"
)
// I2PConfig is a struct which manages I2P configuration options
type I2PConfig struct {
common.I2PConfig
}
func NewConfig(opts ...func(*I2PConfig) error) (*I2PConfig, error) {
var config I2PConfig
config.SamHost = "127.0.0.1"
config.SamPort = 7656
config.SamMin = common.DEFAULT_SAM_MIN
config.SamMax = common.DEFAULT_SAM_MAX
config.TunName = ""
config.TunType = "server"
config.Style = common.SESSION_STYLE_STREAM
config.InLength = 3
config.OutLength = 3
config.InQuantity = 2
config.OutQuantity = 2
config.InVariance = 1
config.OutVariance = 1
config.InBackupQuantity = 3
config.OutBackupQuantity = 3
config.InAllowZeroHop = false
config.OutAllowZeroHop = false
config.EncryptLeaseSet = false
config.LeaseSetKey = ""
config.LeaseSetPrivateKey = ""
config.LeaseSetPrivateSigningKey = ""
config.FastRecieve = false
config.UseCompression = true
config.ReduceIdle = false
config.ReduceIdleTime = 15
config.ReduceIdleQuantity = 4
config.CloseIdle = false
config.CloseIdleTime = 300000
config.MessageReliability = "none"
for _, o := range opts {
if err := o(&config); err != nil {
return nil, err
}
}
return &config, nil
}
// options map
type Options map[string]string
// obtain sam options as list of strings
func (opts Options) AsList() (ls []string) {
for k, v := range opts {
ls = append(ls, fmt.Sprintf("%s=%s", k, v))
}
return
}

View File

@ -1,14 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
import (
"github.com/go-i2p/go-sam-go/datagram"
)
// The DatagramSession implements net.PacketConn. It works almost like ordinary
// UDP, except that datagrams may be at most 31kB large. These datagrams are
// also end-to-end encrypted, signed and includes replay-protection. And they
// are also built to be surveillance-resistant (yey!).
type DatagramSession struct {
datagram.DatagramSession
}

View File

@ -1,78 +0,0 @@
package datagram
import (
"errors"
"net"
"strconv"
"github.com/go-i2p/go-sam-go/common"
"github.com/go-i2p/i2pkeys"
"github.com/sirupsen/logrus"
)
// Creates a new datagram session. udpPort is the UDP port SAM is listening on,
// and if you set it to zero, it will use SAMs standard UDP port.
func (s *SAM) NewDatagramSession(id string, keys i2pkeys.I2PKeys, options []string, udpPort int) (*DatagramSession, error) {
log.WithFields(logrus.Fields{
"id": id,
"udpPort": udpPort,
}).Debug("Creating new DatagramSession")
if udpPort > 65335 || udpPort < 0 {
log.WithField("udpPort", udpPort).Error("Invalid UDP port")
return nil, errors.New("udpPort needs to be in the intervall 0-65335")
}
if udpPort == 0 {
udpPort = 7655
log.Debug("Using default UDP port 7655")
}
lhost, _, err := common.SplitHostPort(s.LocalAddr().String())
if err != nil {
log.WithError(err).Error("Failed to split local host port")
s.Close()
return nil, err
}
lUDPAddr, err := net.ResolveUDPAddr("udp4", lhost+":0")
if err != nil {
log.WithError(err).Error("Failed to resolve local UDP address")
return nil, err
}
udpconn, err := net.ListenUDP("udp4", lUDPAddr)
if err != nil {
log.WithError(err).Error("Failed to listen on UDP")
return nil, err
}
rhost, _, err := common.SplitHostPort(s.RemoteAddr().String())
if err != nil {
log.WithError(err).Error("Failed to split remote host port")
s.Close()
return nil, err
}
rUDPAddr, err := net.ResolveUDPAddr("udp4", rhost+":"+strconv.Itoa(udpPort))
if err != nil {
log.WithError(err).Error("Failed to resolve remote UDP address")
return nil, err
}
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
if err != nil {
log.WithError(err).Error("Failed to get local port")
s.Close()
return nil, err
}
conn, err := s.NewGenericSession("DATAGRAM", id, keys, []string{" PORT=" + lport})
if err != nil {
log.WithError(err).Error("Failed to create generic session")
return nil, err
}
log.WithField("id", id).Info("DatagramSession created successfully")
datagramSession := &DatagramSession{
SAM: s,
UDPConn: udpconn,
SAMUDPAddress: rUDPAddr,
RemoteI2PAddr: nil,
}
datagramSession.Conn = conn
return datagramSession, nil
// return &DatagramSession{s.address, id, conn, udpconn, keys, rUDPAddr, nil}, nil
}

View File

@ -1,5 +0,0 @@
package datagram
import "github.com/go-i2p/logger"
var log = logger.GetGoI2PLogger()

View File

@ -1,209 +0,0 @@
package datagram
import (
"bytes"
"errors"
"net"
"time"
"github.com/go-i2p/go-sam-go/common"
"github.com/go-i2p/i2pkeys"
"github.com/sirupsen/logrus"
)
func (s *DatagramSession) B32() string {
b32 := s.DestinationKeys.Addr().Base32()
log.WithField("b32", b32).Debug("Generated B32 address")
return b32
}
func (s *DatagramSession) Dial(net, addr string) (*DatagramSession, error) {
log.WithFields(logrus.Fields{
"net": net,
"addr": addr,
}).Debug("Dialing address")
netaddr, err := s.Lookup(addr)
if err != nil {
log.WithError(err).Error("Lookup failed")
return nil, err
}
return s.DialI2PRemote(net, netaddr)
}
func (s *DatagramSession) DialRemote(net, addr string) (net.PacketConn, error) {
log.WithFields(logrus.Fields{
"net": net,
"addr": addr,
}).Debug("Dialing remote address")
netaddr, err := s.Lookup(addr)
if err != nil {
log.WithError(err).Error("Lookup failed")
return nil, err
}
return s.DialI2PRemote(net, netaddr)
}
func (s *DatagramSession) DialI2PRemote(net string, addr net.Addr) (*DatagramSession, error) {
log.WithFields(logrus.Fields{
"net": net,
"addr": addr,
}).Debug("Dialing I2P remote address")
switch addr.(type) {
case *i2pkeys.I2PAddr:
s.RemoteI2PAddr = addr.(*i2pkeys.I2PAddr)
case i2pkeys.I2PAddr:
i2paddr := addr.(i2pkeys.I2PAddr)
s.RemoteI2PAddr = &i2paddr
}
return s, nil
}
func (s *DatagramSession) RemoteAddr() net.Addr {
log.WithField("remoteAddr", s.RemoteI2PAddr).Debug("Getting remote address")
return s.RemoteI2PAddr
}
// Reads one datagram sent to the destination of the DatagramSession. Returns
// the number of bytes read, from what address it was sent, or an error.
// implements net.PacketConn
func (s *DatagramSession) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
log.Debug("Reading datagram")
// extra bytes to read the remote address of incomming datagram
buf := make([]byte, len(b)+4096)
for {
// very basic protection: only accept incomming UDP messages from the IP of the SAM bridge
var saddr *net.UDPAddr
n, saddr, err = s.UDPConn.ReadFromUDP(buf)
if err != nil {
log.WithError(err).Error("Failed to read from UDP")
return 0, i2pkeys.I2PAddr(""), err
}
if bytes.Equal(saddr.IP, s.SAMUDPAddress.IP) {
continue
}
break
}
i := bytes.IndexByte(buf, byte('\n'))
if i > 4096 || i > n {
log.Error("Could not parse incoming message remote address")
return 0, i2pkeys.I2PAddr(""), errors.New("Could not parse incomming message remote address.")
}
raddr, err := i2pkeys.NewI2PAddrFromString(string(buf[:i]))
if err != nil {
log.WithError(err).Error("Could not parse incoming message remote address")
return 0, i2pkeys.I2PAddr(""), errors.New("Could not parse incomming message remote address: " + err.Error())
}
// shift out the incomming address to contain only the data received
if (n - i + 1) > len(b) {
copy(b, buf[i+1:i+1+len(b)])
return n - (i + 1), raddr, errors.New("Datagram did not fit into your buffer.")
} else {
copy(b, buf[i+1:n])
log.WithField("bytesRead", n-(i+1)).Debug("Datagram read successfully")
return n - (i + 1), raddr, nil
}
}
func (s *DatagramSession) Accept() (net.Conn, error) {
log.Debug("Accept called on DatagramSession")
return s, nil
}
func (s *DatagramSession) Read(b []byte) (n int, err error) {
log.Debug("Reading from DatagramSession")
rint, _, rerr := s.ReadFrom(b)
return rint, rerr
}
// Sends one signed datagram to the destination specified. At the time of
// writing, maximum size is 31 kilobyte, but this may change in the future.
// Implements net.PacketConn.
func (s *DatagramSession) WriteTo(b []byte, addr net.Addr) (n int, err error) {
log.WithFields(logrus.Fields{
"addr": addr,
"datagramLen": len(b),
}).Debug("Writing datagram")
header := []byte("3.1 " + s.ID() + " " + addr.String() + "\n")
msg := append(header, b...)
n, err = s.UDPConn.WriteToUDP(msg, s.SAMUDPAddress)
if err != nil {
log.WithError(err).Error("Failed to write to UDP")
} else {
log.WithField("bytesWritten", n).Debug("Datagram written successfully")
}
return n, err
}
func (s *DatagramSession) Write(b []byte) (int, error) {
log.WithField("dataLen", len(b)).Debug("Writing to DatagramSession")
return s.WriteTo(b, s.RemoteI2PAddr)
}
// Closes the DatagramSession. Implements net.PacketConn
func (s *DatagramSession) Close() error {
log.Debug("Closing DatagramSession")
err := s.Conn.Close()
err2 := s.UDPConn.Close()
if err != nil {
log.WithError(err).Error("Failed to close connection")
return err
}
if err2 != nil {
log.WithError(err2).Error("Failed to close UDP connection")
}
return err2
}
// Returns the I2P destination of the DatagramSession.
func (s *DatagramSession) LocalI2PAddr() i2pkeys.I2PAddr {
addr := s.DestinationKeys.Addr()
log.WithField("localI2PAddr", addr).Debug("Getting local I2P address")
return addr
}
// Implements net.PacketConn
func (s *DatagramSession) LocalAddr() net.Addr {
return s.LocalI2PAddr()
}
func (s *DatagramSession) Addr() net.Addr {
return s.LocalI2PAddr()
}
func (s *DatagramSession) Lookup(name string) (a net.Addr, err error) {
log.WithField("name", name).Debug("Looking up address")
var sam *common.SAM
sam, err = common.NewSAM(s.Sam())
if err == nil {
defer sam.Close()
a, err = sam.Lookup(name)
}
log.WithField("address", a).Debug("Lookup successful")
return
}
// Sets read and write deadlines for the DatagramSession. Implements
// net.PacketConn and does the same thing. Setting write deadlines for datagrams
// is seldom done.
func (s *DatagramSession) SetDeadline(t time.Time) error {
log.WithField("deadline", t).Debug("Setting deadline")
return s.UDPConn.SetDeadline(t)
}
// Sets read deadline for the DatagramSession. Implements net.PacketConn
func (s *DatagramSession) SetReadDeadline(t time.Time) error {
log.WithField("readDeadline", t).Debug("Setting read deadline")
return s.UDPConn.SetReadDeadline(t)
}
// Sets the write deadline for the DatagramSession. Implements net.Packetconn.
func (s *DatagramSession) SetWriteDeadline(t time.Time) error {
log.WithField("writeDeadline", t).Debug("Setting write deadline")
return s.UDPConn.SetWriteDeadline(t)
}
func (s *DatagramSession) SetWriteBuffer(bytes int) error {
log.WithField("bytes", bytes).Debug("Setting write buffer")
return s.UDPConn.SetWriteBuffer(bytes)
}

View File

@ -1,21 +0,0 @@
package datagram
import (
"net"
"github.com/go-i2p/go-sam-go/common"
"github.com/go-i2p/i2pkeys"
)
type SAM common.SAM
// The DatagramSession implements net.PacketConn. It works almost like ordinary
// UDP, except that datagrams may be at most 31kB large. These datagrams are
// also end-to-end encrypted, signed and includes replay-protection. And they
// are also built to be surveillance-resistant (yey!).
type DatagramSession struct {
*SAM
UDPConn *net.UDPConn // used to deliver datagrams
SAMUDPAddress *net.UDPAddr // the SAM bridge UDP-port
RemoteI2PAddr *i2pkeys.I2PAddr // optional remote I2P address
}

View File

@ -1,183 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
import (
"fmt"
"testing"
"time"
)
func Test_DatagramServerClient(t *testing.T) {
if testing.Short() {
return
}
fmt.Println("Test_DatagramServerClient")
sam, err := NewSAM(yoursam)
if err != nil {
t.Fail()
return
}
defer sam.Close()
keys, err := sam.NewKeys()
if err != nil {
t.Fail()
return
}
// fmt.Println("\tServer: My address: " + keys.Addr().Base32())
fmt.Println("\tServer: Creating tunnel")
ds, err := sam.NewDatagramSession("DGserverTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0)
if err != nil {
fmt.Println("Server: Failed to create tunnel: " + err.Error())
t.Fail()
return
}
c, w := make(chan bool), make(chan bool)
go func(c, w chan (bool)) {
sam2, err := NewSAM(yoursam)
if err != nil {
c <- false
return
}
defer sam2.Close()
keys, err := sam2.NewKeys()
if err != nil {
c <- false
return
}
fmt.Println("\tClient: Creating tunnel")
ds2, err := sam2.NewDatagramSession("DGclientTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0)
if err != nil {
c <- false
return
}
defer ds2.Close()
// fmt.Println("\tClient: Servers address: " + ds.LocalAddr().Base32())
// fmt.Println("\tClient: Clients address: " + ds2.LocalAddr().Base32())
fmt.Println("\tClient: Tries to send datagram to server")
for {
select {
default:
_, err = ds2.WriteTo([]byte("Hello datagram-world! <3 <3 <3 <3 <3 <3"), ds.LocalAddr())
if err != nil {
fmt.Println("\tClient: Failed to send datagram: " + err.Error())
c <- false
return
}
time.Sleep(5 * time.Second)
case <-w:
fmt.Println("\tClient: Sent datagram, quitting.")
return
}
}
c <- true
}(c, w)
buf := make([]byte, 512)
fmt.Println("\tServer: ReadFrom() waiting...")
n, _, err := ds.ReadFrom(buf)
w <- true
if err != nil {
fmt.Println("\tServer: Failed to ReadFrom(): " + err.Error())
t.Fail()
return
}
fmt.Println("\tServer: Received datagram: " + string(buf[:n]))
// fmt.Println("\tServer: Senders address was: " + saddr.Base32())
}
func ExampleDatagramSession() {
// Creates a new DatagramSession, which behaves just like a net.PacketConn.
const samBridge = "127.0.0.1:7656"
sam, err := NewSAM(samBridge)
if err != nil {
fmt.Println(err.Error())
return
}
keys, err := sam.NewKeys()
if err != nil {
fmt.Println(err.Error())
return
}
myself := keys.Addr()
// See the example Option_* variables.
dg, err := sam.NewDatagramSession("DGTUN", keys, Options_Small, 0)
if err != nil {
fmt.Println(err.Error())
return
}
someone, err := sam.Lookup("zzz.i2p")
if err != nil {
fmt.Println(err.Error())
return
}
dg.WriteTo([]byte("Hello stranger!"), someone)
dg.WriteTo([]byte("Hello myself!"), myself)
buf := make([]byte, 31*1024)
n, _, err := dg.ReadFrom(buf)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println("Got message: '" + string(buf[:n]) + "'")
fmt.Println("Got message: " + string(buf[:n]))
return
// Output:
// Got message: Hello myself!
}
func ExampleMiniDatagramSession() {
// Creates a new DatagramSession, which behaves just like a net.PacketConn.
const samBridge = "127.0.0.1:7656"
sam, err := NewSAM(samBridge)
if err != nil {
fmt.Println(err.Error())
return
}
keys, err := sam.NewKeys()
if err != nil {
fmt.Println(err.Error())
return
}
myself := keys.Addr()
// See the example Option_* variables.
dg, err := sam.NewDatagramSession("MINIDGTUN", keys, Options_Small, 0)
if err != nil {
fmt.Println(err.Error())
return
}
someone, err := sam.Lookup("zzz.i2p")
if err != nil {
fmt.Println(err.Error())
return
}
err = dg.SetWriteBuffer(14 * 1024)
if err != nil {
fmt.Println(err.Error())
return
}
dg.WriteTo([]byte("Hello stranger!"), someone)
dg.WriteTo([]byte("Hello myself!"), myself)
buf := make([]byte, 31*1024)
n, _, err := dg.ReadFrom(buf)
if err != nil {
fmt.Println(err.Error())
return
}
log.Println("Got message: '" + string(buf[:n]) + "'")
fmt.Println("Got message: " + string(buf[:n]))
return
// Output:
// Got message: Hello myself!
}

View File

@ -1,437 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
import (
"strconv"
"strings"
"github.com/samber/oops"
"github.com/sirupsen/logrus"
)
// Option is a SAMEmit Option
type Option func(*SAMEmit) error
// SetType sets the type of the forwarder server
func SetType(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if s == "STREAM" {
c.Style = s
log.WithField("style", s).Debug("Set session style")
return nil
} else if s == "DATAGRAM" {
c.Style = s
log.WithField("style", s).Debug("Set session style")
return nil
} else if s == "RAW" {
c.Style = s
log.WithField("style", s).Debug("Set session style")
return nil
}
log.WithField("style", s).Error("Invalid session style")
return oops.Errorf("Invalid session STYLE=%s, must be STREAM, DATAGRAM, or RAW", s)
}
}
// SetSAMAddress sets the SAM address all-at-once
func SetSAMAddress(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
sp := strings.Split(s, ":")
if len(sp) > 2 {
log.WithField("address", s).Error("Invalid SAM address")
return oops.Errorf("Invalid address string: %s", s)
}
if len(sp) == 2 {
port, err := strconv.Atoi(sp[1])
if err != nil {
log.WithField("port", sp[1]).Error("Invalid SAM port: non-number")
return oops.Errorf("Invalid SAM port %s; non-number", sp[1])
}
c.I2PConfig.SamPort = port
}
c.I2PConfig.SamHost = sp[0]
log.WithFields(logrus.Fields{
"host": c.I2PConfig.SamHost,
"port": c.I2PConfig.SamPort,
}).Debug("Set SAM address")
return nil
}
}
// SetSAMHost sets the host of the SAMEmit's SAM bridge
func SetSAMHost(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.SamHost = s
log.WithField("host", s).Debug("Set SAM host")
return nil
}
}
// SetSAMPort sets the port of the SAMEmit's SAM bridge using a string
func SetSAMPort(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
port, err := strconv.Atoi(s)
if err != nil {
log.WithField("port", s).Error("Invalid SAM port: non-number")
return oops.Errorf("Invalid SAM port %s; non-number", s)
}
if port < 65536 && port > -1 {
c.I2PConfig.SamPort = port
log.WithField("port", s).Debug("Set SAM port")
return nil
}
log.WithField("port", port).Error("Invalid SAM port")
return oops.Errorf("Invalid SAM port: out of range")
}
}
// SetName sets the host of the SAMEmit's SAM bridge
func SetName(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.TunName = s
log.WithField("name", s).Debug("Set tunnel name")
return nil
}
}
// SetInLength sets the number of hops inbound
func SetInLength(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u < 7 && u >= 0 {
c.I2PConfig.InLength = u
log.WithField("inLength", u).Debug("Set inbound tunnel length")
return nil
}
log.WithField("inLength", u).Error("Invalid inbound tunnel length")
return oops.Errorf("Invalid inbound tunnel length: out of range")
}
}
// SetOutLength sets the number of hops outbound
func SetOutLength(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u < 7 && u >= 0 {
c.I2PConfig.OutLength = u
log.WithField("outLength", u).Debug("Set outbound tunnel length")
return nil
}
log.WithField("outLength", u).Error("Invalid outbound tunnel length")
return oops.Errorf("Invalid outbound tunnel length: out of range")
}
}
// SetInVariance sets the variance of a number of hops inbound
func SetInVariance(i int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if i < 7 && i > -7 {
c.I2PConfig.InVariance = i
log.WithField("inVariance", i).Debug("Set inbound tunnel variance")
return nil
}
log.WithField("inVariance", i).Error("Invalid inbound tunnel variance")
return oops.Errorf("Invalid inbound tunnel variance: out of range")
}
}
// SetOutVariance sets the variance of a number of hops outbound
func SetOutVariance(i int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if i < 7 && i > -7 {
c.I2PConfig.OutVariance = i
log.WithField("outVariance", i).Debug("Set outbound tunnel variance")
return nil
}
log.WithField("outVariance", i).Error("Invalid outbound tunnel variance")
return oops.Errorf("Invalid outbound tunnel variance: out of range")
}
}
// SetInQuantity sets the inbound tunnel quantity
func SetInQuantity(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u <= 16 && u > 0 {
c.I2PConfig.InQuantity = u
log.WithField("inQuantity", u).Debug("Set inbound tunnel quantity")
return nil
}
log.WithField("inQuantity", u).Error("Invalid inbound tunnel quantity")
return oops.Errorf("Invalid inbound tunnel quantity: out of range")
}
}
// SetOutQuantity sets the outbound tunnel quantity
func SetOutQuantity(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u <= 16 && u > 0 {
c.I2PConfig.OutQuantity = u
log.WithField("outQuantity", u).Debug("Set outbound tunnel quantity")
return nil
}
log.WithField("outQuantity", u).Error("Invalid outbound tunnel quantity")
return oops.Errorf("Invalid outbound tunnel quantity: out of range")
}
}
// SetInBackups sets the inbound tunnel backups
func SetInBackups(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u < 6 && u >= 0 {
c.I2PConfig.InBackupQuantity = u
log.WithField("inBackups", u).Debug("Set inbound tunnel backups")
return nil
}
log.WithField("inBackups", u).Error("Invalid inbound tunnel backup quantity")
return oops.Errorf("Invalid inbound tunnel backup quantity: out of range")
}
}
// SetOutBackups sets the inbound tunnel backups
func SetOutBackups(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u < 6 && u >= 0 {
c.I2PConfig.OutBackupQuantity = u
log.WithField("outBackups", u).Debug("Set outbound tunnel backups")
return nil
}
log.WithField("outBackups", u).Error("Invalid outbound tunnel backup quantity")
return oops.Errorf("Invalid outbound tunnel backup quantity: out of range")
}
}
// SetEncrypt tells the router to use an encrypted leaseset
func SetEncrypt(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
c.I2PConfig.EncryptLeaseSet = true
return nil
}
c.I2PConfig.EncryptLeaseSet = false
log.WithField("encrypt", b).Debug("Set lease set encryption")
return nil
}
}
// SetLeaseSetKey sets the host of the SAMEmit's SAM bridge
func SetLeaseSetKey(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.LeaseSetKey = s
log.WithField("leaseSetKey", s).Debug("Set lease set key")
return nil
}
}
// SetLeaseSetPrivateKey sets the host of the SAMEmit's SAM bridge
func SetLeaseSetPrivateKey(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.LeaseSetPrivateKey = s
log.WithField("leaseSetPrivateKey", s).Debug("Set lease set private key")
return nil
}
}
// SetLeaseSetPrivateSigningKey sets the host of the SAMEmit's SAM bridge
func SetLeaseSetPrivateSigningKey(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.LeaseSetPrivateSigningKey = s
log.WithField("leaseSetPrivateSigningKey", s).Debug("Set lease set private signing key")
return nil
}
}
// SetMessageReliability sets the host of the SAMEmit's SAM bridge
func SetMessageReliability(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.MessageReliability = s
log.WithField("messageReliability", s).Debug("Set message reliability")
return nil
}
}
// SetAllowZeroIn tells the tunnel to accept zero-hop peers
func SetAllowZeroIn(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
c.I2PConfig.InAllowZeroHop = true
return nil
}
c.I2PConfig.InAllowZeroHop = false
log.WithField("allowZeroIn", b).Debug("Set allow zero-hop inbound")
return nil
}
}
// SetAllowZeroOut tells the tunnel to accept zero-hop peers
func SetAllowZeroOut(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
c.I2PConfig.OutAllowZeroHop = true
return nil
}
c.I2PConfig.OutAllowZeroHop = false
log.WithField("allowZeroOut", b).Debug("Set allow zero-hop outbound")
return nil
}
}
// SetCompress tells clients to use compression
func SetCompress(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
c.I2PConfig.UseCompression = true
return nil
}
c.I2PConfig.UseCompression = false
log.WithField("compress", b).Debug("Set compression")
return nil
}
}
// SetFastRecieve tells clients to use compression
func SetFastRecieve(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
c.I2PConfig.FastRecieve = true
return nil
}
c.I2PConfig.FastRecieve = false
log.WithField("fastReceive", b).Debug("Set fast receive")
return nil
}
}
// SetReduceIdle tells the connection to reduce it's tunnels during extended idle time.
func SetReduceIdle(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
c.I2PConfig.ReduceIdle = true
return nil
}
c.I2PConfig.ReduceIdle = false
log.WithField("reduceIdle", b).Debug("Set reduce idle")
return nil
}
}
// SetReduceIdleTime sets the time to wait before reducing tunnels to idle levels
func SetReduceIdleTime(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.ReduceIdleTime = 300000
if u >= 6 {
idleTime := (u * 60) * 1000
c.I2PConfig.ReduceIdleTime = idleTime
log.WithField("reduceIdleTime", idleTime).Debug("Set reduce idle time")
return nil
}
log.WithField("minutes", u).Error("Invalid reduce idle timeout")
return oops.Errorf("Invalid reduce idle timeout (Measured in minutes) %v", u)
}
}
// SetReduceIdleTimeMs sets the time to wait before reducing tunnels to idle levels in milliseconds
func SetReduceIdleTimeMs(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.ReduceIdleTime = 300000
if u >= 300000 {
c.I2PConfig.ReduceIdleTime = u
log.WithField("reduceIdleTimeMs", u).Debug("Set reduce idle time in milliseconds")
return nil
}
log.WithField("milliseconds", u).Error("Invalid reduce idle timeout")
return oops.Errorf("Invalid reduce idle timeout (Measured in milliseconds) %v", u)
}
}
// SetReduceIdleQuantity sets minimum number of tunnels to reduce to during idle time
func SetReduceIdleQuantity(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u < 5 {
c.I2PConfig.ReduceIdleQuantity = u
log.WithField("reduceIdleQuantity", u).Debug("Set reduce idle quantity")
return nil
}
log.WithField("quantity", u).Error("Invalid reduce tunnel quantity")
return oops.Errorf("Invalid reduce idle tunnel quantity: out of range")
}
}
// SetCloseIdle tells the connection to close it's tunnels during extended idle time.
func SetCloseIdle(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
c.I2PConfig.CloseIdle = true
return nil
}
c.I2PConfig.CloseIdle = false
return nil
}
}
// SetCloseIdleTime sets the time to wait before closing tunnels to idle levels
func SetCloseIdleTime(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.CloseIdleTime = 300000
if u >= 6 {
idleTime := (u * 60) * 1000
c.I2PConfig.CloseIdleTime = idleTime
log.WithFields(logrus.Fields{
"minutes": u,
"milliseconds": idleTime,
}).Debug("Set close idle time")
return nil
}
log.WithField("minutes", u).Error("Invalid close idle timeout")
return oops.Errorf("Invalid close idle timeout (Measured in minutes) %v", u)
}
}
// SetCloseIdleTimeMs sets the time to wait before closing tunnels to idle levels in milliseconds
func SetCloseIdleTimeMs(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.CloseIdleTime = 300000
if u >= 300000 {
c.I2PConfig.CloseIdleTime = u
log.WithField("closeIdleTimeMs", u).Debug("Set close idle time in milliseconds")
return nil
}
return oops.Errorf("Invalid close idle timeout (Measured in milliseconds) %v", u)
}
}
// SetAccessListType tells the system to treat the AccessList as a whitelist
func SetAccessListType(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if s == "whitelist" {
c.I2PConfig.AccessListType = "whitelist"
log.Debug("Set access list type to whitelist")
return nil
} else if s == "blacklist" {
c.I2PConfig.AccessListType = "blacklist"
log.Debug("Set access list type to blacklist")
return nil
} else if s == "none" {
c.I2PConfig.AccessListType = ""
log.Debug("Set access list type to none")
return nil
} else if s == "" {
c.I2PConfig.AccessListType = ""
log.Debug("Set access list type to none")
return nil
}
return oops.Errorf("Invalid Access list type (whitelist, blacklist, none)")
}
}
// SetAccessList tells the system to treat the AccessList as a whitelist
func SetAccessList(s []string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if len(s) > 0 {
for _, a := range s {
c.I2PConfig.AccessList = append(c.I2PConfig.AccessList, a)
}
log.WithField("accessList", s).Debug("Set access list")
return nil
}
log.Debug("No access list set (empty list provided)")
return nil
}
}

142
emit.go
View File

@ -1,142 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
import (
"fmt"
"net"
"strings"
"github.com/go-i2p/go-sam-go/common"
"github.com/sirupsen/logrus"
)
type SAMEmit struct {
common.SAMEmit
}
func (e *SAMEmit) SamOptionsString() string {
optStr := strings.Join(e.I2PConfig.Print(), " ")
log.WithField("optStr", optStr).Debug("Generated option string")
return optStr
}
func (e *SAMEmit) Hello() string {
hello := fmt.Sprintf("HELLO VERSION MIN=%s MAX=%s \n", e.I2PConfig.MinSAM(), e.I2PConfig.MaxSAM())
log.WithField("hello", hello).Debug("Generated HELLO command")
return hello
}
func (e *SAMEmit) HelloBytes() []byte {
return []byte(e.Hello())
}
func (e *SAMEmit) GenerateDestination() string {
dest := fmt.Sprintf("DEST GENERATE %s \n", e.I2PConfig.SignatureType())
log.WithField("destination", dest).Debug("Generated DEST GENERATE command")
return dest
}
func (e *SAMEmit) GenerateDestinationBytes() []byte {
return []byte(e.GenerateDestination())
}
func (e *SAMEmit) Lookup(name string) string {
lookup := fmt.Sprintf("NAMING LOOKUP NAME=%s \n", name)
log.WithField("lookup", lookup).Debug("Generated NAMING LOOKUP command")
return lookup
}
func (e *SAMEmit) LookupBytes(name string) []byte {
return []byte(e.Lookup(name))
}
func (e *SAMEmit) Create() string {
create := fmt.Sprintf(
// //1 2 3 4 5 6 7
"SESSION CREATE %s%s%s%s%s%s%s \n",
e.I2PConfig.SessionStyle(), // 1
e.I2PConfig.FromPort(), // 2
e.I2PConfig.ToPort(), // 3
e.I2PConfig.ID(), // 4
e.I2PConfig.DestinationKey(), // 5
e.I2PConfig.SignatureType(), // 6
e.SamOptionsString(), // 7
)
log.WithField("create", create).Debug("Generated SESSION CREATE command")
return create
}
func (e *SAMEmit) CreateBytes() []byte {
fmt.Println("sam command: " + e.Create())
return []byte(e.Create())
}
func (e *SAMEmit) Connect(dest string) string {
connect := fmt.Sprintf(
"STREAM CONNECT ID=%s %s %s DESTINATION=%s \n",
e.I2PConfig.ID(),
e.I2PConfig.FromPort(),
e.I2PConfig.ToPort(),
dest,
)
log.WithField("connect", connect).Debug("Generated STREAM CONNECT command")
return connect
}
func (e *SAMEmit) ConnectBytes(dest string) []byte {
return []byte(e.Connect(dest))
}
func (e *SAMEmit) Accept() string {
accept := fmt.Sprintf(
"STREAM ACCEPT ID=%s %s %s",
e.I2PConfig.ID(),
e.I2PConfig.FromPort(),
e.I2PConfig.ToPort(),
)
log.WithField("accept", accept).Debug("Generated STREAM ACCEPT command")
return accept
}
func (e *SAMEmit) AcceptBytes() []byte {
return []byte(e.Accept())
}
func NewEmit(opts ...func(*SAMEmit) error) (*SAMEmit, error) {
var emit SAMEmit
for _, o := range opts {
if err := o(&emit); err != nil {
log.WithError(err).Error("Failed to apply option")
return nil, err
}
}
log.Debug("New SAMEmit instance created")
return &emit, nil
}
func IgnorePortError(err error) error {
if err == nil {
return nil
}
if strings.Contains(err.Error(), "missing port in address") {
log.Debug("Ignoring 'missing port in address' error")
err = nil
}
return err
}
func SplitHostPort(hostport string) (string, string, error) {
host, port, err := net.SplitHostPort(hostport)
if err != nil {
if IgnorePortError(err) == nil {
log.WithField("host", hostport).Debug("Using full string as host, port set to 0")
host = hostport
port = "0"
}
}
log.WithFields(logrus.Fields{
"host": host,
"port": port,
}).Debug("Split host and port")
return host, port, nil
}

7
log.go
View File

@ -1,7 +0,0 @@
package sam3
import (
"github.com/go-i2p/logger"
)
var log = logger.GetGoI2PLogger()

View File

@ -1,28 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
import (
"github.com/go-i2p/go-sam-go/primary"
)
const (
session_ADDOK = "SESSION STATUS RESULT=OK"
)
// Represents a primary session.
type PrimarySession struct {
*primary.PrimarySession
}
var PrimarySessionSwitch = "MASTER"
func (p *PrimarySession) NewStreamSubSession(id string) (*StreamSession, error) {
log.WithField("id", id).Debug("NewStreamSubSession called")
session, err := p.PrimarySession.NewStreamSubSession(id)
if err != nil {
return nil, err
}
return &StreamSession{
StreamSession: session,
}, nil
}

View File

@ -1,3 +0,0 @@
package primary
const SESSION_ADDOK = "SESSION STATUS RESULT=OK"

View File

@ -1,73 +0,0 @@
package primary
import (
"errors"
"net"
"strconv"
"github.com/go-i2p/go-sam-go/common"
"github.com/go-i2p/go-sam-go/datagram"
"github.com/sirupsen/logrus"
)
// Creates a new datagram session. udpPort is the UDP port SAM is listening on,
// and if you set it to zero, it will use SAMs standard UDP port.
func (s *PrimarySession) NewDatagramSubSession(id string, udpPort int) (*datagram.DatagramSession, error) {
log.WithFields(logrus.Fields{"id": id, "udpPort": udpPort}).Debug("NewDatagramSubSession called")
if udpPort > 65335 || udpPort < 0 {
log.WithField("udpPort", udpPort).Error("Invalid UDP port")
return nil, errors.New("udpPort needs to be in the intervall 0-65335")
}
if udpPort == 0 {
udpPort = 7655
log.Debug("Using default UDP port 7655")
}
lhost, _, err := common.SplitHostPort(s.conn.LocalAddr().String())
if err != nil {
log.WithError(err).Error("Failed to split local host port")
s.Close()
return nil, err
}
lUDPAddr, err := net.ResolveUDPAddr("udp4", lhost+":0")
if err != nil {
log.WithError(err).Error("Failed to resolve local UDP address")
return nil, err
}
udpconn, err := net.ListenUDP("udp4", lUDPAddr)
if err != nil {
log.WithError(err).Error("Failed to listen on UDP")
return nil, err
}
rhost, _, err := common.SplitHostPort(s.conn.RemoteAddr().String())
if err != nil {
log.WithError(err).Error("Failed to split remote host port")
s.Close()
return nil, err
}
rUDPAddr, err := net.ResolveUDPAddr("udp4", rhost+":"+strconv.Itoa(udpPort))
if err != nil {
log.WithError(err).Error("Failed to resolve remote UDP address")
return nil, err
}
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
if err != nil {
log.WithError(err).Error("Failed to get local port")
s.Close()
return nil, err
}
conn, err := s.NewGenericSubSession("DATAGRAM", id, []string{"PORT=" + lport})
if err != nil {
log.WithError(err).Error("Failed to create new generic sub-session")
return nil, err
}
log.WithFields(logrus.Fields{"id": id, "localPort": lport}).Debug("Created new datagram sub-session")
datagramSession := &datagram.DatagramSession{
SAM: (*datagram.SAM)(s.SAM),
SAMUDPAddress: rUDPAddr,
UDPConn: udpconn,
RemoteI2PAddr: nil,
}
datagramSession.Conn = conn
return datagramSession, nil
}

View File

@ -1,105 +0,0 @@
package primary
import (
"net"
"strings"
"github.com/go-i2p/go-sam-go/common"
"github.com/go-i2p/go-sam-go/datagram"
"github.com/samber/oops"
"github.com/sirupsen/logrus"
)
func (sam *PrimarySession) Dial(network, addr string) (net.Conn, error) {
log.WithFields(logrus.Fields{"network": network, "addr": addr}).Debug("Dial() called")
if network == "udp" || network == "udp4" || network == "udp6" {
// return sam.DialUDPI2P(network, network+addr[0:4], addr)
return sam.DialUDPI2P(network, network+addr[0:4], addr)
}
if network == "tcp" || network == "tcp4" || network == "tcp6" {
// return sam.DialTCPI2P(network, network+addr[0:4], addr)
return sam.DialTCPI2P(network, network+addr[0:4], addr)
}
log.WithField("network", network).Error("Invalid network type")
return nil, oops.Errorf("Error: Must specify a valid network type")
}
// DialTCP implements x/dialer
func (sam *PrimarySession) DialTCP(network string, laddr, raddr net.Addr) (net.Conn, error) {
log.WithFields(logrus.Fields{"network": network, "laddr": laddr, "raddr": raddr}).Debug("DialTCP() called")
ts, ok := sam.stsess[network+raddr.String()[0:4]]
var err error
if !ok {
ts, err = sam.NewUniqueStreamSubSession(network + raddr.String()[0:4])
if err != nil {
log.WithError(err).Error("Failed to create new unique stream sub-session")
return nil, err
}
sam.stsess[network+raddr.String()[0:4]] = ts
ts, _ = sam.stsess[network+raddr.String()[0:4]]
}
return ts.Dial(network, raddr.String())
}
func (sam *PrimarySession) DialTCPI2P(network, laddr, raddr string) (net.Conn, error) {
log.WithFields(logrus.Fields{"network": network, "laddr": laddr, "raddr": raddr}).Debug("DialTCPI2P() called")
ts, ok := sam.stsess[network+raddr[0:4]]
var err error
if !ok {
ts, err = sam.NewUniqueStreamSubSession(network + laddr)
if err != nil {
log.WithError(err).Error("Failed to create new unique stream sub-session")
return nil, err
}
sam.stsess[network+raddr[0:4]] = ts
ts, _ = sam.stsess[network+raddr[0:4]]
}
return ts.Dial(network, raddr)
}
// DialUDP implements x/dialer
func (sam *PrimarySession) DialUDP(network string, laddr, raddr net.Addr) (net.PacketConn, error) {
log.WithFields(logrus.Fields{"network": network, "laddr": laddr, "raddr": raddr}).Debug("DialUDP() called")
ds, ok := sam.dgsess[network+raddr.String()[0:4]]
var err error
if !ok {
ds, err = sam.NewDatagramSubSession(network+raddr.String()[0:4], 0)
if err != nil {
log.WithError(err).Error("Failed to create new datagram sub-session")
return nil, err
}
sam.dgsess[network+raddr.String()[0:4]] = ds
ds, _ = sam.dgsess[network+raddr.String()[0:4]]
}
return ds.Dial(network, raddr.String())
}
func (sam *PrimarySession) DialUDPI2P(network, laddr, raddr string) (*datagram.DatagramSession, error) {
log.WithFields(logrus.Fields{"network": network, "laddr": laddr, "raddr": raddr}).Debug("DialUDPI2P() called")
ds, ok := sam.dgsess[network+raddr[0:4]]
var err error
if !ok {
ds, err = sam.NewDatagramSubSession(network+laddr, 0)
if err != nil {
log.WithError(err).Error("Failed to create new datagram sub-session")
return nil, err
}
sam.dgsess[network+raddr[0:4]] = ds
ds, _ = sam.dgsess[network+raddr[0:4]]
}
return ds.Dial(network, raddr)
}
func (s *PrimarySession) Lookup(name string) (a net.Addr, err error) {
log.WithField("name", name).Debug("Lookup() called")
var sam *common.SAM
name = strings.Split(name, ":")[0]
sam, err = common.NewSAM(s.samAddr)
if err == nil {
log.WithField("addr", a).Debug("Lookup successful")
defer sam.Close()
a, err = sam.Lookup(name)
}
log.WithError(err).Error("Lookup failed")
return
}

View File

@ -1,101 +0,0 @@
package primary
import (
"errors"
"net"
"strings"
"github.com/sirupsen/logrus"
"github.com/go-i2p/go-sam-go/common"
)
// Creates a new session with the style of either "STREAM", "DATAGRAM" or "RAW",
// for a new I2P tunnel with name id, using the cypher keys specified, with the
// I2CP/streaminglib-options as specified. Extra arguments can be specified by
// setting extra to something else than []string{}.
// This sam3 instance is now a session
func (sam *PrimarySession) NewGenericSubSession(style, id string, extras []string) (net.Conn, error) {
log.WithFields(logrus.Fields{"style": style, "id": id, "extras": extras}).Debug("newGenericSubSession called")
return sam.NewGenericSubSessionWithSignature(style, id, extras)
}
func (sam *PrimarySession) NewGenericSubSessionWithSignature(style, id string, extras []string) (net.Conn, error) {
log.WithFields(logrus.Fields{"style": style, "id": id, "extras": extras}).Debug("newGenericSubSessionWithSignature called")
return sam.NewGenericSubSessionWithSignatureAndPorts(style, id, "0", "0", extras)
}
// Creates a new session with the style of either "STREAM", "DATAGRAM" or "RAW",
// for a new I2P tunnel with name id, using the cypher keys specified, with the
// I2CP/streaminglib-options as specified. Extra arguments can be specified by
// setting extra to something else than []string{}.
// This sam3 instance is now a session
func (sam *PrimarySession) NewGenericSubSessionWithSignatureAndPorts(style, id, from, to string, extras []string) (net.Conn, error) {
log.WithFields(logrus.Fields{"style": style, "id": id, "from": from, "to": to, "extras": extras}).Debug("newGenericSubSessionWithSignatureAndPorts called")
conn := sam.conn
fp := ""
tp := ""
if from != "0" && from != "" {
fp = " FROM_PORT=" + from
}
if to != "0" && to != "" {
tp = " TO_PORT=" + to
}
scmsg := []byte("SESSION ADD STYLE=" + style + " ID=" + id + fp + tp + " " + strings.Join(extras, " ") + "\n")
log.WithField("message", string(scmsg)).Debug("Sending SESSION ADD message")
for m, i := 0, 0; m != len(scmsg); i++ {
if i == 15 {
conn.Close()
log.Error("Writing to SAM failed after 15 attempts")
return nil, errors.New("writing to SAM failed")
}
n, err := conn.Write(scmsg[m:])
if err != nil {
log.WithError(err).Error("Failed to write to SAM connection")
conn.Close()
return nil, err
}
m += n
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
log.WithError(err).Error("Failed to read from SAM connection")
conn.Close()
return nil, err
}
text := string(buf[:n])
log.WithField("response", text).Debug("Received response from SAM")
// log.Println("SAM:", text)
if strings.HasPrefix(text, SESSION_ADDOK) {
//if sam.keys.String() != text[len(common.SESSION_ADDOK):len(text)-1] {
//conn.Close()
//return nil, errors.New("SAMv3 created a tunnel with keys other than the ones we asked it for")
//}
log.Debug("Session added successfully")
return conn, nil //&StreamSession{id, conn, keys, nil, sync.RWMutex{}, nil}, nil
} else if text == common.SESSION_DUPLICATE_ID {
log.Error("Duplicate tunnel name")
conn.Close()
return nil, errors.New("Duplicate tunnel name")
} else if text == common.SESSION_DUPLICATE_DEST {
log.Error("Duplicate destination")
conn.Close()
return nil, errors.New("Duplicate destination")
} else if text == common.SESSION_INVALID_KEY {
log.Error("Invalid key - Primary Session")
conn.Close()
return nil, errors.New("Invalid key - Primary Session")
} else if strings.HasPrefix(text, common.SESSION_I2P_ERROR) {
log.WithField("error", text[len(common.SESSION_I2P_ERROR):]).Error("I2P error")
conn.Close()
return nil, errors.New("I2P error " + text[len(common.SESSION_I2P_ERROR):])
} else {
log.WithField("reply", text).Error("Unable to parse SAMv3 reply")
conn.Close()
return nil, errors.New("Unable to parse SAMv3 reply: " + text)
}
}

View File

@ -1,7 +0,0 @@
package primary
import (
"github.com/go-i2p/logger"
)
var log = logger.GetGoI2PLogger()

View File

@ -1,76 +0,0 @@
package primary
import (
"time"
"github.com/go-i2p/go-sam-go/common"
"github.com/go-i2p/go-sam-go/datagram"
"github.com/go-i2p/go-sam-go/stream"
"github.com/go-i2p/i2pkeys"
"github.com/sirupsen/logrus"
)
var PrimarySessionSwitch string = "MASTER"
// Creates a new PrimarySession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewPrimarySession(id string, keys i2pkeys.I2PKeys, options []string) (*PrimarySession, error) {
log.WithFields(logrus.Fields{"id": id, "options": options}).Debug("NewPrimarySession() called")
return sam.newPrimarySession(PrimarySessionSwitch, id, keys, options)
}
func (sam *SAM) newPrimarySession(primarySessionSwitch, id string, keys i2pkeys.I2PKeys, options []string) (*PrimarySession, error) {
log.WithFields(logrus.Fields{
"primarySessionSwitch": primarySessionSwitch,
"id": id,
"options": options,
}).Debug("newPrimarySession() called")
conn, err := sam.NewGenericSession(primarySessionSwitch, id, keys, options)
if err != nil {
log.WithError(err).Error("Failed to create new generic session")
return nil, err
}
return &PrimarySession{
SAM: sam,
samAddr: "",
id: id,
conn: conn,
keys: keys,
Timeout: 0,
Deadline: time.Time{},
sigType: "",
Config: common.SAMEmit{},
stsess: map[string]*stream.StreamSession{},
dgsess: map[string]*datagram.DatagramSession{},
}, nil
}
// Creates a new PrimarySession with the I2CP- and PRIMARYinglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewPrimarySessionWithSignature(id string, keys i2pkeys.I2PKeys, options []string, sigType string) (*PrimarySession, error) {
log.WithFields(logrus.Fields{
"id": id,
"options": options,
"sigType": sigType,
}).Debug("NewPrimarySessionWithSignature() called")
conn, err := sam.NewGenericSessionWithSignature(PrimarySessionSwitch, id, keys, sigType, options)
if err != nil {
log.WithError(err).Error("Failed to create new generic session with signature")
return nil, err
}
return &PrimarySession{
SAM: sam,
samAddr: "",
id: id,
conn: conn,
keys: keys,
Timeout: 0,
Deadline: time.Time{},
sigType: sigType,
Config: common.SAMEmit{},
stsess: map[string]*stream.StreamSession{},
dgsess: map[string]*datagram.DatagramSession{},
}, nil
}

View File

@ -1,74 +0,0 @@
package primary
import (
"errors"
"net"
"strconv"
"github.com/go-i2p/go-sam-go/common"
"github.com/go-i2p/go-sam-go/raw"
"github.com/sirupsen/logrus"
)
// Creates a new raw session. udpPort is the UDP port SAM is listening on,
// and if you set it to zero, it will use SAMs standard UDP port.
func (s *PrimarySession) NewRawSubSession(id string, udpPort int) (*raw.RawSession, error) {
log.WithFields(logrus.Fields{"id": id, "udpPort": udpPort}).Debug("NewRawSubSession called")
if udpPort > 65335 || udpPort < 0 {
log.WithField("udpPort", udpPort).Error("Invalid UDP port")
return nil, errors.New("udpPort needs to be in the intervall 0-65335")
}
if udpPort == 0 {
udpPort = 7655
log.Debug("Using default UDP port 7655")
}
lhost, _, err := common.SplitHostPort(s.conn.LocalAddr().String())
if err != nil {
log.WithError(err).Error("Failed to split local host port")
s.Close()
return nil, err
}
lUDPAddr, err := net.ResolveUDPAddr("udp4", lhost+":0")
if err != nil {
log.WithError(err).Error("Failed to resolve local UDP address")
return nil, err
}
udpconn, err := net.ListenUDP("udp4", lUDPAddr)
if err != nil {
log.WithError(err).Error("Failed to listen on UDP")
return nil, err
}
rhost, _, err := common.SplitHostPort(s.conn.RemoteAddr().String())
if err != nil {
log.WithError(err).Error("Failed to split remote host port")
s.Close()
return nil, err
}
rUDPAddr, err := net.ResolveUDPAddr("udp4", rhost+":"+strconv.Itoa(udpPort))
if err != nil {
log.WithError(err).Error("Failed to resolve remote UDP address")
return nil, err
}
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
if err != nil {
log.WithError(err).Error("Failed to get local port")
s.Close()
return nil, err
}
// conn, err := s.newGenericSubSession("RAW", id, s.keys, options, []string{"PORT=" + lport})
conn, err := s.NewGenericSubSession("RAW", id, []string{"PORT=" + lport})
if err != nil {
log.WithError(err).Error("Failed to create new generic sub-session")
return nil, err
}
log.WithFields(logrus.Fields{"id": id, "localPort": lport}).Debug("Created new raw sub-session")
rawSession := &raw.RawSession{
SAM: (*raw.SAM)(s.SAM),
SAMUDPConn: udpconn,
SAMUDPAddr: rUDPAddr,
}
rawSession.Conn = conn
return rawSession, nil
}

View File

@ -1,57 +0,0 @@
package primary
import (
"net"
"github.com/go-i2p/go-sam-go/common"
"github.com/go-i2p/go-sam-go/stream"
"github.com/sirupsen/logrus"
)
// Creates a new stream.StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *PrimarySession) NewStreamSubSession(id string) (*stream.StreamSession, error) {
log.WithField("id", id).Debug("NewStreamSubSession called")
conn, err := sam.NewGenericSubSession("STREAM", id, []string{})
if err != nil {
log.WithError(err).Error("Failed to create new generic sub-session")
return nil, err
}
return newFromPrimary(sam, conn), nil
}
// Creates a new stream.StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *PrimarySession) NewUniqueStreamSubSession(id string) (*stream.StreamSession, error) {
log.WithField("id", id).Debug("NewUniqueStreamSubSession called")
conn, err := sam.NewGenericSubSession("STREAM", id, []string{})
if err != nil {
log.WithError(err).Error("Failed to create new generic sub-session")
return nil, err
}
return newFromPrimary(sam, conn), nil
}
// Creates a new stream.StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *PrimarySession) NewStreamSubSessionWithPorts(id, from, to string) (*stream.StreamSession, error) {
log.WithFields(logrus.Fields{"id": id, "from": from, "to": to}).Debug("NewStreamSubSessionWithPorts called")
conn, err := sam.NewGenericSubSessionWithSignatureAndPorts("STREAM", id, from, to, []string{})
if err != nil {
log.WithError(err).Error("Failed to create new generic sub-session with signature and ports")
return nil, err
}
return newFromPrimary(sam, conn), nil
}
func newFromPrimary(sam *PrimarySession, conn net.Conn) *stream.StreamSession {
streamSession := &stream.StreamSession{
SAM: &stream.SAM{
SAM: (*common.SAM)(sam.SAM),
},
}
streamSession.Conn = conn
return streamSession
}

View File

@ -1,30 +0,0 @@
package primary
import (
"net"
"time"
"github.com/go-i2p/go-sam-go/common"
"github.com/go-i2p/go-sam-go/datagram"
"github.com/go-i2p/go-sam-go/stream"
"github.com/go-i2p/i2pkeys"
)
type SAM common.SAM
// Represents a primary session.
type PrimarySession struct {
*SAM
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam
keys i2pkeys.I2PKeys // i2p destination keys
Timeout time.Duration
Deadline time.Time
sigType string
Config common.SAMEmit
stsess map[string]*stream.StreamSession
dgsess map[string]*datagram.DatagramSession
// from string
// to string
}

View File

@ -1,149 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
import (
"fmt"
"testing"
"time"
)
func Test_PrimaryDatagramServerClient(t *testing.T) {
if testing.Short() {
return
}
fmt.Println("Test_PrimaryDatagramServerClient")
earlysam, err := NewSAM(yoursam)
if err != nil {
t.Fail()
return
}
defer earlysam.Close()
keys, err := earlysam.NewKeys()
if err != nil {
t.Fail()
return
}
sam, err := earlysam.NewPrimarySession("PrimaryTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
t.Fail()
return
}
defer sam.Close()
// fmt.Println("\tServer: My address: " + keys.Addr().Base32())
fmt.Println("\tServer: Creating tunnel")
ds, err := sam.NewDatagramSubSession("PrimaryTunnel"+RandString(), 0)
if err != nil {
fmt.Println("Server: Failed to create tunnel: " + err.Error())
t.Fail()
return
}
defer ds.Close()
c, w := make(chan bool), make(chan bool)
go func(c, w chan (bool)) {
sam2, err := NewSAM(yoursam)
if err != nil {
c <- false
return
}
defer sam2.Close()
keys, err := sam2.NewKeys()
if err != nil {
c <- false
return
}
fmt.Println("\tClient: Creating tunnel")
ds2, err := sam2.NewDatagramSession("PRIMARYClientTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0)
if err != nil {
c <- false
return
}
defer ds2.Close()
// fmt.Println("\tClient: Servers address: " + ds.LocalAddr().Base32())
// fmt.Println("\tClient: Clients address: " + ds2.LocalAddr().Base32())
fmt.Println("\tClient: Tries to send primary to server")
for {
select {
default:
_, err = ds2.WriteTo([]byte("Hello primary-world! <3 <3 <3 <3 <3 <3"), ds.LocalAddr())
if err != nil {
fmt.Println("\tClient: Failed to send primary: " + err.Error())
c <- false
return
}
time.Sleep(5 * time.Second)
case <-w:
fmt.Println("\tClient: Sent primary, quitting.")
return
}
}
c <- true
}(c, w)
buf := make([]byte, 512)
fmt.Println("\tServer: ReadFrom() waiting...")
n, _, err := ds.ReadFrom(buf)
w <- true
if err != nil {
fmt.Println("\tServer: Failed to ReadFrom(): " + err.Error())
t.Fail()
return
}
fmt.Println("\tServer: Received primary: " + string(buf[:n]))
// fmt.Println("\tServer: Senders address was: " + saddr.Base32())
}
func ExamplePrimaryDatagramSession() {
// Creates a new PrimarySession, then creates a Datagram subsession on top of it
const samBridge = "127.0.0.1:7656"
earlysam, err := NewSAM(samBridge)
if err != nil {
fmt.Println(err.Error())
return
}
defer earlysam.Close()
keys, err := earlysam.NewKeys()
if err != nil {
fmt.Println(err.Error())
return
}
myself := keys.Addr()
sam, err := earlysam.NewPrimarySession("PrimaryTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
fmt.Println(err.Error())
return
}
defer sam.Close()
// See the example Option_* variables.
dg, err := sam.NewDatagramSubSession("DGTUN"+RandString(), 0)
if err != nil {
fmt.Println(err.Error())
return
}
defer dg.Close()
someone, err := earlysam.Lookup("zzz.i2p")
if err != nil {
fmt.Println(err.Error())
return
}
dg.WriteTo([]byte("Hello stranger!"), someone)
dg.WriteTo([]byte("Hello myself!"), myself)
buf := make([]byte, 31*1024)
n, _, err := dg.ReadFrom(buf)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println("Got message: '" + string(buf[:n]) + "'")
fmt.Println("Got message: " + string(buf[:n]))
return
// Output:
// Got message: Hello myself!
}

View File

@ -1,307 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
import (
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"time"
)
func Test_PrimaryStreamingDial(t *testing.T) {
if testing.Short() {
return
}
fmt.Println("Test_PrimaryStreamingDial")
earlysam, err := NewSAM(yoursam)
if err != nil {
t.Fail()
return
}
defer earlysam.Close()
keys, err := earlysam.NewKeys()
if err != nil {
t.Fail()
return
}
sam, err := earlysam.NewPrimarySession("PrimaryTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
t.Fail()
return
}
defer sam.Close()
fmt.Println("\tBuilding tunnel")
ss, err := sam.NewStreamSubSession("primaryStreamTunnel")
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
defer ss.Close()
fmt.Println("\tNotice: This may fail if your I2P node is not well integrated in the I2P network.")
fmt.Println("\tLooking up i2p-projekt.i2p")
forumAddr, err := earlysam.Lookup("i2p-projekt.i2p")
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
fmt.Println("\tDialing i2p-projekt.i2p(", forumAddr.Base32(), forumAddr.DestHash().Hash(), ")")
conn, err := ss.DialI2P(forumAddr)
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
defer conn.Close()
fmt.Println("\tSending HTTP GET /")
if _, err := conn.Write([]byte("GET /\n")); err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if !strings.Contains(strings.ToLower(string(buf[:n])), "http") && !strings.Contains(strings.ToLower(string(buf[:n])), "html") {
fmt.Printf("\tProbably failed to StreamSession.DialI2P(i2p-projekt.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
} else {
fmt.Println("\tRead HTTP/HTML from i2p-projekt.i2p")
}
}
func Test_PrimaryStreamingServerClient(t *testing.T) {
if testing.Short() {
return
}
fmt.Println("Test_StreamingServerClient")
earlysam, err := NewSAM(yoursam)
if err != nil {
t.Fail()
return
}
defer earlysam.Close()
keys, err := earlysam.NewKeys()
if err != nil {
t.Fail()
return
}
sam, err := earlysam.NewPrimarySession("PrimaryServerClientTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
t.Fail()
return
}
defer sam.Close()
fmt.Println("\tServer: Creating tunnel")
ss, err := sam.NewUniqueStreamSubSession("PrimaryServerClientTunnel")
if err != nil {
return
}
defer ss.Close()
time.Sleep(time.Second * 10)
c, w := make(chan bool), make(chan bool)
go func(c, w chan (bool)) {
if !(<-w) {
return
}
/*
sam2, err := NewSAM(yoursam)
if err != nil {
c <- false
return
}
defer sam2.Close()
keys, err := sam2.NewKeys()
if err != nil {
c <- false
return
}
*/
fmt.Println("\tClient: Creating tunnel")
ss2, err := sam.NewStreamSubSession("primaryExampleClientTun")
if err != nil {
c <- false
return
}
defer ss2.Close()
fmt.Println("\tClient: Connecting to server")
conn, err := ss2.DialI2P(ss.Addr())
if err != nil {
c <- false
return
}
fmt.Println("\tClient: Connected to tunnel")
defer conn.Close()
_, err = conn.Write([]byte("Hello world <3 <3 <3 <3 <3 <3"))
if err != nil {
c <- false
return
}
c <- true
}(c, w)
l, err := ss.Listen()
if err != nil {
fmt.Println("ss.Listen(): " + err.Error())
t.Fail()
w <- false
return
}
defer l.Close()
w <- true
fmt.Println("\tServer: Accept()ing on tunnel")
conn, err := l.Accept()
if err != nil {
t.Fail()
fmt.Println("Failed to Accept(): " + err.Error())
return
}
defer conn.Close()
buf := make([]byte, 512)
n, err := conn.Read(buf)
fmt.Printf("\tClient exited successfully: %t\n", <-c)
fmt.Println("\tServer: received from Client: " + string(buf[:n]))
}
func ExamplePrimaryStreamSession() {
// Creates a new StreamingSession, dials to idk.i2p and gets a SAMConn
// which behaves just like a normal net.Conn.
const samBridge = "127.0.0.1:7656"
earlysam, err := NewSAM(yoursam)
if err != nil {
log.Fatal(err.Error())
return
}
defer earlysam.Close()
keys, err := earlysam.NewKeys()
if err != nil {
log.Fatal(err.Error())
return
}
sam, err := earlysam.NewPrimarySession("PrimaryStreamSessionTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
log.Fatal(err.Error())
return
}
defer sam.Close()
conn, err := sam.Dial("tcp", "idk.i2p") // someone.Base32())
if err != nil {
fmt.Println(err.Error())
return
}
defer conn.Close()
fmt.Println("Sending HTTP GET /")
if _, err := conn.Write([]byte("GET /\n")); err != nil {
fmt.Println(err.Error())
return
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if !strings.Contains(strings.ToLower(string(buf[:n])), "http") && !strings.Contains(strings.ToLower(string(buf[:n])), "html") {
fmt.Printf("Probably failed to StreamSession.DialI2P(idk.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
log.Printf("Probably failed to StreamSession.DialI2P(idk.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
} else {
fmt.Println("Read HTTP/HTML from idk.i2p")
log.Println("Read HTTP/HTML from idk.i2p")
}
// Output:
// Sending HTTP GET /
// Read HTTP/HTML from idk.i2p
}
func ExamplePrimaryStreamListener() {
// One server Accept()ing on a StreamListener, and one client that Dials
// through I2P to the server. Server writes "Hello world!" through a SAMConn
// (which implements net.Conn) and the client prints the message.
const samBridge = "127.0.0.1:7656"
var ss *StreamSession
go func() {
earlysam, err := NewSAM(yoursam)
if err != nil {
log.Fatal(err.Error())
return
}
defer earlysam.Close()
keys, err := earlysam.NewKeys()
if err != nil {
log.Fatal(err.Error())
return
}
sam, err := earlysam.NewPrimarySession("PrimaryListenerTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
log.Fatal(err.Error())
return
}
defer sam.Close()
ss, err = sam.NewStreamSubSession("PrimaryListenerServerTunnel2")
if err != nil {
fmt.Println(err.Error())
return
}
defer ss.Close()
l, err := ss.Listen()
if err != nil {
fmt.Println(err.Error())
return
}
defer l.Close()
// fmt.Println("Serving on primary listener", l.Addr().String())
if err := http.Serve(l, &exitHandler{}); err != nil {
fmt.Println(err.Error())
}
}()
time.Sleep(time.Second * 10)
latesam, err := NewSAM(yoursam)
if err != nil {
log.Fatal(err.Error())
return
}
defer latesam.Close()
keys2, err := latesam.NewKeys()
if err != nil {
log.Fatal(err.Error())
return
}
sc, err := latesam.NewStreamSession("PrimaryListenerClientTunnel2", keys2, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
fmt.Println(err.Error())
return
}
defer sc.Close()
client := http.Client{
Transport: &http.Transport{
Dial: sc.Dial,
},
}
// resp, err := client.Get("http://" + "idk.i2p") //ss.Addr().Base32())
resp, err := client.Get("http://" + ss.Addr().Base32())
if err != nil {
fmt.Println(err.Error())
return
}
defer resp.Body.Close()
r, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println("Got response: " + string(r))
// Output:
// Got response: Hello world!
}
type exitHandler struct{}
func (e *exitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello world!"))
}

16
raw.go
View File

@ -1,16 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
import (
"github.com/go-i2p/go-sam-go/raw"
)
// The RawSession provides no authentication of senders, and there is no sender
// address attached to datagrams, so all communication is anonymous. The
// messages send are however still endpoint-to-endpoint encrypted. You
// need to figure out a way to identify and authenticate clients yourself, iff
// that is needed. Raw datagrams may be at most 32 kB in size. There is no
// overhead of authentication, which is the reason to use this..
type RawSession struct {
*raw.RawSession
}

View File

@ -1,7 +0,0 @@
package raw
import (
"github.com/go-i2p/logger"
)
var log = logger.GetGoI2PLogger()

View File

@ -1,73 +0,0 @@
package raw
import (
"errors"
"net"
"strconv"
"github.com/go-i2p/go-sam-go/common"
"github.com/go-i2p/i2pkeys"
"github.com/sirupsen/logrus"
)
// Creates a new raw session. udpPort is the UDP port SAM is listening on,
// and if you set it to zero, it will use SAMs standard UDP port.
func (s *SAM) NewRawSession(id string, keys i2pkeys.I2PKeys, options []string, udpPort int) (*RawSession, error) {
log.WithFields(logrus.Fields{"id": id, "udpPort": udpPort}).Debug("Creating new RawSession")
if udpPort > 65335 || udpPort < 0 {
log.WithField("udpPort", udpPort).Error("Invalid UDP port")
return nil, errors.New("udpPort needs to be in the interval 0-65335")
}
if udpPort == 0 {
udpPort = 7655
log.Debug("Using default UDP port 7655")
}
lhost, _, err := common.SplitHostPort(s.LocalAddr().String())
if err != nil {
log.Debug("Using default UDP port 7655")
s.Close()
return nil, err
}
lUDPAddr, err := net.ResolveUDPAddr("udp4", lhost+":0")
if err != nil {
log.WithError(err).Error("Failed to resolve local UDP address")
return nil, err
}
udpconn, err := net.ListenUDP("udp4", lUDPAddr)
if err != nil {
log.WithError(err).Error("Failed to listen on UDP")
return nil, err
}
rhost, _, err := common.SplitHostPort(s.RemoteAddr().String())
if err != nil {
log.WithError(err).Error("Failed to split remote host port")
s.Close()
return nil, err
}
rUDPAddr, err := net.ResolveUDPAddr("udp4", rhost+":"+strconv.Itoa(udpPort))
if err != nil {
log.WithError(err).Error("Failed to resolve remote UDP address")
return nil, err
}
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
if err != nil {
log.WithError(err).Error("Failed to get local port")
return nil, err
}
conn, err := s.NewGenericSession("RAW", id, keys, []string{"PORT=" + lport})
if err != nil {
log.WithError(err).Error("Failed to create new generic session")
return nil, err
}
log.WithFields(logrus.Fields{
"id": id,
"localPort": lport,
"remoteUDPAddr": rUDPAddr,
}).Debug("Created new RawSession")
rawSession := &RawSession{
SAM: s,
}
rawSession.Conn = conn
return rawSession, nil
}

View File

@ -1,21 +0,0 @@
package raw
import (
"net"
"github.com/go-i2p/go-sam-go/common"
)
type SAM common.SAM
// The RawSession provides no authentication of senders, and there is no sender
// address attached to datagrams, so all communication is anonymous. The
// messages send are however still endpoint-to-endpoint encrypted. You
// need to figure out a way to identify and authenticate clients yourself, iff
// that is needed. Raw datagrams may be at most 32 kB in size. There is no
// overhead of authentication, which is the reason to use this..
type RawSession struct {
*SAM
SAMUDPConn *net.UDPConn // used to deliver datagrams
SAMUDPAddr *net.UDPAddr // the SAM bridge UDP-port
}

View File

@ -1,25 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
type SAMResolver struct {
*SAM
}
func NewSAMResolver(parent *SAM) (*SAMResolver, error) {
log.Debug("Creating new SAMResolver from existing SAM instance")
var s SAMResolver
s.SAM = parent
return &s, nil
}
func NewFullSAMResolver(address string) (*SAMResolver, error) {
log.WithField("address", address).Debug("Creating new full SAMResolver")
var s SAMResolver
var err error
s.SAM, err = NewSAM(address)
if err != nil {
log.WithError(err).Error("Failed to create new SAM instance")
return nil, err
}
return &s, nil
}

81
sam3.go
View File

@ -1,81 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
import (
"math/rand"
"github.com/go-i2p/go-sam-go/common"
"github.com/go-i2p/go-sam-go/datagram"
"github.com/go-i2p/go-sam-go/primary"
"github.com/go-i2p/go-sam-go/stream"
"github.com/go-i2p/i2pkeys"
)
// Used for controlling I2Ps SAMv3.
type SAM struct {
*common.SAM
}
// Creates a new stream session by wrapping stream.NewStreamSession
func (s *SAM) NewStreamSession(id string, keys i2pkeys.I2PKeys, options []string) (*StreamSession, error) {
sam := &stream.SAM{
SAM: s.SAM,
}
ss, err := sam.NewStreamSession(id, keys, options)
if err != nil {
return nil, err
}
streamSession := &StreamSession{
StreamSession: ss,
}
return streamSession, nil
}
// Creates a new Datagram session by wrapping datagram.NewDatagramSession
func (s *SAM) NewDatagramSession(id string, keys i2pkeys.I2PKeys, options []string, port int) (*DatagramSession, error) {
sam := datagram.SAM(*s.SAM)
dgs, err := sam.NewDatagramSession(id, keys, options, port)
if err != nil {
return nil, err
}
datagramSession := DatagramSession{
DatagramSession: *dgs,
}
return &datagramSession, nil
}
func (s *SAM) NewPrimarySession(id string, keys i2pkeys.I2PKeys, options []string) (*PrimarySession, error) {
sam := primary.SAM(*s.SAM)
ps, err := sam.NewPrimarySession(id, keys, options)
if err != nil {
return nil, err
}
primarySession := PrimarySession{
PrimarySession: ps,
}
return &primarySession, nil
}
func RandString() string {
letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
n := 4
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
log.WithField("randomString", string(b)).Debug("Generated random string")
return string(b)
}
// Creates a new controller for the I2P routers SAM bridge.
func NewSAM(address string) (*SAM, error) {
is, err := common.NewSAM(address)
if err != nil {
log.WithError(err).Error("Failed to create new SAM instance")
return nil, err
}
s := &SAM{
SAM: is,
}
return s, nil
}

View File

@ -1,165 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
import (
"fmt"
"testing"
"time"
)
const yoursam = "127.0.0.1:7656"
func Test_Basic(t *testing.T) {
fmt.Println("Test_Basic")
fmt.Println("\tAttaching to SAM at " + yoursam)
sam, err := NewSAM(yoursam)
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
fmt.Println("\tCreating new keys...")
keys, err := sam.NewKeys()
if err != nil {
fmt.Println(err.Error())
t.Fail()
} else {
fmt.Println("\tAddress created: " + keys.Addr().Base32())
fmt.Println("\tI2PKeys: " + string(keys.String())[:50] + "(...etc)")
}
addr2, err := sam.Lookup("zzz.i2p")
if err != nil {
fmt.Println(err.Error())
t.Fail()
} else {
fmt.Println("\tzzz.i2p = " + addr2.Base32())
}
if err := sam.Close(); err != nil {
fmt.Println(err.Error())
t.Fail()
}
}
/*
func Test_GenericSession(t *testing.T) {
if testing.Short() {
return
}
fmt.Println("Test_GenericSession")
sam, err := NewSAM(yoursam)
if err != nil {
fmt.Println(err.Error)
t.Fail()
return
}
keys, err := sam.NewKeys()
if err != nil {
fmt.Println(err.Error())
t.Fail()
} else {
conn1, err := sam.newGenericSession("STREAM", "testTun", keys, []string{})
if err != nil {
fmt.Println(err.Error())
t.Fail()
} else {
conn1.Close()
}
conn2, err := sam.newGenericSession("STREAM", "testTun", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=1", "outbound.lengthVariance=1", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
fmt.Println(err.Error())
t.Fail()
} else {
conn2.Close()
}
conn3, err := sam.newGenericSession("DATAGRAM", "testTun", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=1", "outbound.lengthVariance=1", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
fmt.Println(err.Error())
t.Fail()
} else {
conn3.Close()
}
}
if err := sam.Close(); err != nil {
fmt.Println(err.Error())
t.Fail()
}
}
*/
func Test_RawServerClient(t *testing.T) {
if testing.Short() {
return
}
fmt.Println("Test_RawServerClient")
sam, err := NewSAM(yoursam)
if err != nil {
t.Fail()
return
}
defer sam.Close()
keys, err := sam.NewKeys()
if err != nil {
t.Fail()
return
}
fmt.Println("\tServer: Creating tunnel")
rs, err := sam.NewDatagramSession("RAWserverTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0)
if err != nil {
fmt.Println("Server: Failed to create tunnel: " + err.Error())
t.Fail()
return
}
c, w := make(chan bool), make(chan bool)
go func(c, w chan (bool)) {
sam2, err := NewSAM(yoursam)
if err != nil {
c <- false
return
}
defer sam2.Close()
keys, err := sam2.NewKeys()
if err != nil {
c <- false
return
}
fmt.Println("\tClient: Creating tunnel")
rs2, err := sam2.NewDatagramSession("RAWclientTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0)
if err != nil {
c <- false
return
}
defer rs2.Close()
fmt.Println("\tClient: Tries to send raw datagram to server")
for {
select {
default:
_, err = rs2.WriteTo([]byte("Hello raw-world! <3 <3 <3 <3 <3 <3"), rs.LocalAddr())
if err != nil {
fmt.Println("\tClient: Failed to send raw datagram: " + err.Error())
c <- false
return
}
time.Sleep(5 * time.Second)
case <-w:
fmt.Println("\tClient: Sent raw datagram, quitting.")
return
}
}
c <- true
}(c, w)
buf := make([]byte, 512)
fmt.Println("\tServer: Read() waiting...")
n, _, err := rs.ReadFrom(buf)
w <- true
if err != nil {
fmt.Println("\tServer: Failed to Read(): " + err.Error())
t.Fail()
return
}
fmt.Println("\tServer: Received datagram: " + string(buf[:n]))
// fmt.Println("\tServer: Senders address was: " + saddr.Base32())
}

View File

@ -1,18 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
import (
"github.com/go-i2p/go-sam-go/stream"
)
// Represents a streaming session.
type StreamSession struct {
*stream.StreamSession
}
/*
func (s *StreamSession) Cancel() chan *StreamSession {
ch := make(chan *StreamSession)
ch <- s
return ch
}*/

View File

@ -77,13 +77,13 @@ func (s *StreamSession) Dial(n, addr string) (c net.Conn, err error) {
// Dials to an I2P destination and returns a SAMConn, which implements a net.Conn.
func (s *StreamSession) DialI2P(addr i2pkeys.I2PAddr) (*StreamConn, error) {
log.WithField("addr", addr).Debug("DialI2P called")
sam, err := common.NewSAM(s.Sam())
sam, err := common.NewSAM(s.SAM().Sam())
if err != nil {
log.WithError(err).Error("Failed to create new SAM instance")
return nil, err
}
conn := sam.Conn
_, err = conn.Write([]byte("STREAM CONNECT ID=" + s.ID() + s.FromPort() + s.ToPort() + " DESTINATION=" + addr.Base64() + " SILENT=false\n"))
_, err = conn.Write([]byte("STREAM CONNECT ID=" + s.SAM().ID() + s.SAM().FromPort() + s.SAM().ToPort() + " DESTINATION=" + addr.Base64() + " SILENT=false\n"))
if err != nil {
log.WithError(err).Error("Failed to write STREAM CONNECT command")
conn.Close()

View File

@ -4,7 +4,7 @@ import "github.com/sirupsen/logrus"
// create a new stream listener to accept inbound connections
func (s *StreamSession) Listen() (*StreamListener, error) {
log.WithFields(logrus.Fields{"id": s.ID(), "laddr": s.Addr()}).Debug("Creating new StreamListener")
log.WithFields(logrus.Fields{"id": s.SAM().ID(), "laddr": s.Addr()}).Debug("Creating new StreamListener")
return &StreamListener{
session: s,
}, nil

View File

@ -14,17 +14,17 @@ import (
)
func (l *StreamListener) From() string {
return l.session.Fromport
return l.session.SAM().Fromport
}
func (l *StreamListener) To() string {
return l.session.Toport
return l.session.SAM().Toport
}
// get our address
// implements net.Listener
func (l *StreamListener) Addr() net.Addr {
return l.session.DestinationKeys.Addr()
return l.session.Addr()
}
// implements net.Listener
@ -40,12 +40,12 @@ func (l *StreamListener) Accept() (net.Conn, error) {
// accept a new inbound connection
func (l *StreamListener) AcceptI2P() (*StreamConn, error) {
log.Debug("StreamListener.AcceptI2P() called")
s, err := common.NewSAM(l.session.Sam())
s, err := common.NewSAM(l.session.SAM().Sam())
if err == nil {
log.Debug("Connected to SAM bridge")
// we connected to sam
// send accept() command
_, err = io.WriteString(s.Conn, "STREAM ACCEPT ID="+l.session.ID()+" SILENT=false\n")
_, err = io.WriteString(s.Conn, "STREAM ACCEPT ID="+l.session.SAM().ID()+" SILENT=false\n")
if err != nil {
log.WithError(err).Error("Failed to send STREAM ACCEPT command")
s.Close()
@ -67,8 +67,8 @@ func (l *StreamListener) AcceptI2P() (*StreamConn, error) {
destline, err := rd.ReadString(10)
if err == nil {
dest := common.ExtractDest(destline)
l.session.Fromport = common.ExtractPairString(destline, "FROM_PORT")
l.session.Toport = common.ExtractPairString(destline, "TO_PORT")
l.session.SAM().Fromport = common.ExtractPairString(destline, "FROM_PORT")
l.session.SAM().Toport = common.ExtractPairString(destline, "TO_PORT")
// return wrapped connection
dest = strings.Trim(dest, "\n")
log.WithFields(logrus.Fields{

View File

@ -11,49 +11,49 @@ import (
// Read reads data from the stream.
func (s *StreamSession) Read(buf []byte) (int, error) {
return s.Conn.Read(buf)
return s.SAM().Conn.Read(buf)
}
// Write sends data over the stream.
func (s *StreamSession) Write(data []byte) (int, error) {
return s.Conn.Write(data)
return s.SAM().Conn.Write(data)
}
func (s *StreamSession) SetDeadline(t time.Time) error {
log.WithField("deadline", t).Debug("Setting deadline for StreamSession")
return s.Conn.SetDeadline(t)
return s.SAM().Conn.SetDeadline(t)
}
func (s *StreamSession) SetReadDeadline(t time.Time) error {
log.WithField("readDeadline", t).Debug("Setting read deadline for StreamSession")
return s.Conn.SetReadDeadline(t)
return s.SAM().Conn.SetReadDeadline(t)
}
func (s *StreamSession) SetWriteDeadline(t time.Time) error {
log.WithField("writeDeadline", t).Debug("Setting write deadline for StreamSession")
return s.Conn.SetWriteDeadline(t)
return s.SAM().Conn.SetWriteDeadline(t)
}
func (s *StreamSession) From() string {
return s.Fromport
return s.SAM().Fromport
}
func (s *StreamSession) To() string {
return s.Toport
return s.SAM().Toport
}
func (s *StreamSession) SignatureType() string {
return s.SignatureType()
return s.SAM().SignatureType()
}
func (s *StreamSession) Close() error {
log.WithField("id", s.ID()).Debug("Closing StreamSession")
return s.Conn.Close()
log.WithField("id", s.SAM().ID()).Debug("Closing StreamSession")
return s.SAM().Conn.Close()
}
// Returns the I2P destination (the address) of the stream session
func (s *StreamSession) Addr() i2pkeys.I2PAddr {
return s.Addr()
return s.Keys().Address
}
func (s *StreamSession) LocalAddr() net.Addr {
@ -62,13 +62,13 @@ func (s *StreamSession) LocalAddr() net.Addr {
// Returns the keys associated with the stream session
func (s *StreamSession) Keys() i2pkeys.I2PKeys {
return *s.DestinationKeys
return *s.SAM().DestinationKeys
}
// lookup name, convenience function
func (s *StreamSession) Lookup(name string) (i2pkeys.I2PAddr, error) {
log.WithField("name", name).Debug("Looking up address")
sam, err := common.NewSAM(s.Sam())
sam, err := common.NewSAM(s.SAM().Sam())
if err == nil {
addr, err := sam.Lookup(name)
defer sam.Close()

View File

@ -7,7 +7,7 @@ import (
// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewStreamSession(id string, keys i2pkeys.I2PKeys, options []string) (*StreamSession, error) {
func (sam SAM) NewStreamSession(id string, keys i2pkeys.I2PKeys, options []string) (*StreamSession, error) {
log.WithFields(logrus.Fields{"id": id, "options": options}).Debug("Creating new StreamSession")
conn, err := sam.NewGenericSession("STREAM", id, keys, []string{})
if err != nil {
@ -15,7 +15,7 @@ func (sam *SAM) NewStreamSession(id string, keys i2pkeys.I2PKeys, options []stri
}
log.WithField("id", id).Debug("Created new StreamSession")
streamSession := &StreamSession{
SAM: sam,
sam: sam,
}
streamSession.Conn = conn
return streamSession, nil
@ -23,7 +23,7 @@ func (sam *SAM) NewStreamSession(id string, keys i2pkeys.I2PKeys, options []stri
// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewStreamSessionWithSignature(id string, keys i2pkeys.I2PKeys, options []string, sigType string) (*StreamSession, error) {
func (sam SAM) NewStreamSessionWithSignature(id string, keys i2pkeys.I2PKeys, options []string, sigType string) (*StreamSession, error) {
log.WithFields(logrus.Fields{"id": id, "options": options, "sigType": sigType}).Debug("Creating new StreamSession with signature")
conn, err := sam.NewGenericSessionWithSignature("STREAM", id, keys, sigType, []string{})
if err != nil {
@ -32,7 +32,7 @@ func (sam *SAM) NewStreamSessionWithSignature(id string, keys i2pkeys.I2PKeys, o
log.WithFields(logrus.Fields{"id": id, "sigType": sigType}).Debug("Created new StreamSession with signature")
log.WithField("id", id).Debug("Created new StreamSession")
streamSession := &StreamSession{
SAM: sam,
sam: sam,
}
streamSession.Conn = conn
return streamSession, nil
@ -40,7 +40,7 @@ func (sam *SAM) NewStreamSessionWithSignature(id string, keys i2pkeys.I2PKeys, o
// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewStreamSessionWithSignatureAndPorts(id, from, to string, keys i2pkeys.I2PKeys, options []string, sigType string) (*StreamSession, error) {
func (sam SAM) NewStreamSessionWithSignatureAndPorts(id, from, to string, keys i2pkeys.I2PKeys, options []string, sigType string) (*StreamSession, error) {
log.WithFields(logrus.Fields{"id": id, "from": from, "to": to, "options": options, "sigType": sigType}).Debug("Creating new StreamSession with signature and ports")
conn, err := sam.NewGenericSessionWithSignatureAndPorts("STREAM", id, from, to, keys, sigType, []string{})
if err != nil {
@ -49,7 +49,7 @@ func (sam *SAM) NewStreamSessionWithSignatureAndPorts(id, from, to string, keys
log.WithFields(logrus.Fields{"id": id, "from": from, "to": to, "sigType": sigType}).Debug("Created new StreamSession with signature and ports")
log.WithField("id", id).Debug("Created new StreamSession")
streamSession := &StreamSession{
SAM: sam,
sam: sam,
}
streamSession.Conn = conn
return streamSession, nil

View File

@ -2,8 +2,6 @@ package stream
import (
"testing"
"github.com/go-i2p/go-sam-go/common"
)
func TestNewStreamSession_Integration(t *testing.T) {
@ -36,13 +34,10 @@ func TestNewStreamSession_Integration(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create a fresh SAM connection for each test
commonSam, err := common.NewSAM("127.0.0.1:7656")
sam, err := NewSAM("127.0.0.1:7656")
if err != nil {
t.Fatalf("NewSAM() error = %v", err)
}
defer commonSam.Close()
sam := &SAM{SAM: commonSam}
// Generate keys through the SAM bridge
keys, err := sam.NewKeys()
@ -58,6 +53,7 @@ func TestNewStreamSession_Integration(t *testing.T) {
if err == nil {
session.Close()
}
sam.Close()
})
}
}

View File

@ -9,14 +9,19 @@ import (
)
type SAM struct {
*common.SAM
common.SAM
}
// Represents a streaming session.
type StreamSession struct {
*SAM
sam SAM
Timeout time.Duration
Deadline time.Time
net.Conn
}
func (s *StreamSession) SAM() *SAM {
return &s.sam
}
type StreamListener struct {

View File

@ -1,10 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
import (
"github.com/go-i2p/go-sam-go/stream"
)
type StreamListener struct {
*stream.StreamListener
}

View File

@ -1,284 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
import (
"fmt"
"strings"
"testing"
"github.com/go-i2p/i2pkeys"
)
func Test_StreamingDial(t *testing.T) {
if testing.Short() {
return
}
fmt.Println("Test_StreamingDial")
sam, err := NewSAM(yoursam)
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
defer sam.Close()
keys, err := sam.NewKeys()
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
fmt.Println("\tBuilding tunnel")
ss, err := sam.NewStreamSession("streamTun", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
fmt.Println("\tNotice: This may fail if your I2P node is not well integrated in the I2P network.")
fmt.Println("\tLooking up i2p-projekt.i2p")
forumAddr, err := sam.Lookup("i2p-projekt.i2p")
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
fmt.Println("\tDialing i2p-projekt.i2p(", forumAddr.Base32(), forumAddr.DestHash().Hash(), ")")
conn, err := ss.DialI2P(forumAddr)
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
defer conn.Close()
fmt.Println("\tSending HTTP GET /")
if _, err := conn.Write([]byte("GET /\n")); err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if !strings.Contains(strings.ToLower(string(buf[:n])), "http") && !strings.Contains(strings.ToLower(string(buf[:n])), "html") {
fmt.Printf("\tProbably failed to StreamSession.DialI2P(i2p-projekt.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
} else {
fmt.Println("\tRead HTTP/HTML from i2p-projekt.i2p")
}
}
func Test_StreamingServerClient(t *testing.T) {
if testing.Short() {
return
}
fmt.Println("Test_StreamingServerClient")
sam, err := NewSAM(yoursam)
if err != nil {
t.Fail()
return
}
defer sam.Close()
keys, err := sam.NewKeys()
if err != nil {
t.Fail()
return
}
fmt.Println("\tServer: Creating tunnel")
ss, err := sam.NewStreamSession("serverTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
return
}
c, w := make(chan bool), make(chan bool)
go func(c, w chan (bool)) {
if !(<-w) {
return
}
sam2, err := NewSAM(yoursam)
if err != nil {
c <- false
return
}
defer sam2.Close()
keys, err := sam2.NewKeys()
if err != nil {
c <- false
return
}
fmt.Println("\tClient: Creating tunnel")
ss2, err := sam2.NewStreamSession("clientTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
c <- false
return
}
fmt.Println("\tClient: Connecting to server")
conn, err := ss2.DialI2P(ss.Addr())
if err != nil {
c <- false
return
}
fmt.Println("\tClient: Connected to tunnel")
defer conn.Close()
_, err = conn.Write([]byte("Hello world <3 <3 <3 <3 <3 <3"))
if err != nil {
c <- false
return
}
c <- true
}(c, w)
l, err := ss.Listen()
if err != nil {
fmt.Println("ss.Listen(): " + err.Error())
t.Fail()
w <- false
return
}
defer l.Close()
w <- true
fmt.Println("\tServer: Accept()ing on tunnel")
conn, err := l.Accept()
if err != nil {
t.Fail()
fmt.Println("Failed to Accept(): " + err.Error())
return
}
defer conn.Close()
buf := make([]byte, 512)
n, err := conn.Read(buf)
fmt.Printf("\tClient exited successfully: %t\n", <-c)
fmt.Println("\tServer: received from Client: " + string(buf[:n]))
}
func ExampleStreamSession() {
// Creates a new StreamingSession, dials to idk.i2p and gets a SAMConn
// which behaves just like a normal net.Conn.
const samBridge = "127.0.0.1:7656"
sam, err := NewSAM(samBridge)
if err != nil {
fmt.Println(err.Error())
return
}
defer sam.Close()
keys, err := sam.NewKeys()
if err != nil {
fmt.Println(err.Error())
return
}
// See the example Option_* variables.
ss, err := sam.NewStreamSession("stream_example", keys, Options_Small)
if err != nil {
fmt.Println(err.Error())
return
}
someone, err := sam.Lookup("idk.i2p")
if err != nil {
fmt.Println(err.Error())
return
}
conn, err := ss.DialI2P(someone)
if err != nil {
fmt.Println(err.Error())
return
}
defer conn.Close()
fmt.Println("Sending HTTP GET /")
if _, err := conn.Write([]byte("GET /\n")); err != nil {
fmt.Println(err.Error())
return
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if !strings.Contains(strings.ToLower(string(buf[:n])), "http") && !strings.Contains(strings.ToLower(string(buf[:n])), "html") {
fmt.Printf("Probably failed to StreamSession.DialI2P(idk.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
log.Printf("Probably failed to StreamSession.DialI2P(idk.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
} else {
fmt.Println("Read HTTP/HTML from idk.i2p")
log.Println("Read HTTP/HTML from idk.i2p")
}
return
// Output:
// Sending HTTP GET /
// Read HTTP/HTML from idk.i2p
}
func ExampleStreamListener() {
// One server Accept()ing on a StreamListener, and one client that Dials
// through I2P to the server. Server writes "Hello world!" through a SAMConn
// (which implements net.Conn) and the client prints the message.
const samBridge = "127.0.0.1:7656"
sam, err := NewSAM(samBridge)
if err != nil {
fmt.Println(err.Error())
return
}
defer sam.Close()
keys, err := sam.NewKeys()
if err != nil {
fmt.Println(err.Error())
return
}
quit := make(chan bool)
// Client connecting to the server
go func(server i2pkeys.I2PAddr) {
csam, err := NewSAM(samBridge)
if err != nil {
fmt.Println(err.Error())
return
}
defer csam.Close()
keys, err := csam.NewKeys()
if err != nil {
fmt.Println(err.Error())
return
}
cs, err := csam.NewStreamSession("client_example", keys, Options_Small)
if err != nil {
fmt.Println(err.Error())
quit <- false
return
}
conn, err := cs.DialI2P(server)
if err != nil {
fmt.Println(err.Error())
quit <- false
return
}
buf := make([]byte, 256)
n, err := conn.Read(buf)
if err != nil {
fmt.Println(err.Error())
quit <- false
return
}
fmt.Println(string(buf[:n]))
quit <- true
}(keys.Addr()) // end of client
ss, err := sam.NewStreamSession("server_example", keys, Options_Small)
if err != nil {
fmt.Println(err.Error())
return
}
l, err := ss.Listen()
if err != nil {
fmt.Println(err.Error())
return
}
conn, err := l.Accept()
if err != nil {
fmt.Println(err.Error())
return
}
conn.Write([]byte("Hello world!"))
<-quit // waits for client to die, for example only
// Output:
// Hello world!
}

View File

@ -1,120 +0,0 @@
// package sam3 wraps the original sam3 API from github.com/go-i2p/sam3
package sam3
import (
"net"
"os"
"strings"
"github.com/sirupsen/logrus"
)
// Examples and suggestions for options when creating sessions.
var (
// Suitable options if you are shuffling A LOT of traffic. If unused, this
// will waste your resources.
Options_Humongous = []string{
"inbound.length=3", "outbound.length=3",
"inbound.lengthVariance=1", "outbound.lengthVariance=1",
"inbound.backupQuantity=3", "outbound.backupQuantity=3",
"inbound.quantity=6", "outbound.quantity=6",
}
// Suitable for shuffling a lot of traffic.
Options_Large = []string{
"inbound.length=3", "outbound.length=3",
"inbound.lengthVariance=1", "outbound.lengthVariance=1",
"inbound.backupQuantity=1", "outbound.backupQuantity=1",
"inbound.quantity=4", "outbound.quantity=4",
}
// Suitable for shuffling a lot of traffic quickly with minimum
// anonymity. Uses 1 hop and multiple tunnels.
Options_Wide = []string{
"inbound.length=1", "outbound.length=1",
"inbound.lengthVariance=1", "outbound.lengthVariance=1",
"inbound.backupQuantity=2", "outbound.backupQuantity=2",
"inbound.quantity=3", "outbound.quantity=3",
}
// Suitable for shuffling medium amounts of traffic.
Options_Medium = []string{
"inbound.length=3", "outbound.length=3",
"inbound.lengthVariance=1", "outbound.lengthVariance=1",
"inbound.backupQuantity=0", "outbound.backupQuantity=0",
"inbound.quantity=2", "outbound.quantity=2",
}
// Sensible defaults for most people
Options_Default = []string{
"inbound.length=3", "outbound.length=3",
"inbound.lengthVariance=0", "outbound.lengthVariance=0",
"inbound.backupQuantity=1", "outbound.backupQuantity=1",
"inbound.quantity=1", "outbound.quantity=1",
}
// Suitable only for small dataflows, and very short lasting connections:
// You only have one tunnel in each direction, so if any of the nodes
// through which any of your two tunnels pass through go offline, there will
// be a complete halt in the dataflow, until a new tunnel is built.
Options_Small = []string{
"inbound.length=3", "outbound.length=3",
"inbound.lengthVariance=1", "outbound.lengthVariance=1",
"inbound.backupQuantity=0", "outbound.backupQuantity=0",
"inbound.quantity=1", "outbound.quantity=1",
}
// Does not use any anonymization, you connect directly to others tunnel
// endpoints, thus revealing your identity but not theirs. Use this only
// if you don't care.
Options_Warning_ZeroHop = []string{
"inbound.length=0", "outbound.length=0",
"inbound.lengthVariance=0", "outbound.lengthVariance=0",
"inbound.backupQuantity=0", "outbound.backupQuantity=0",
"inbound.quantity=2", "outbound.quantity=2",
}
)
func getEnv(key, fallback string) string {
value, ok := os.LookupEnv(key)
if !ok {
log.WithFields(logrus.Fields{
"key": key,
"fallback": fallback,
}).Debug("Environment variable not set, using fallback")
return fallback
}
log.WithFields(logrus.Fields{
"key": key,
"value": value,
}).Debug("Retrieved environment variable")
return value
}
var (
SAM_HOST = getEnv("sam_host", "127.0.0.1")
SAM_PORT = getEnv("sam_port", "7656")
)
func SAMDefaultAddr(fallforward string) string {
if fallforward == "" {
addr := net.JoinHostPort(SAM_HOST, SAM_PORT)
log.WithField("addr", addr).Debug("Using default SAM address")
return addr
}
log.WithField("addr", fallforward).Debug("Using fallforward SAM address")
return fallforward
}
func GenerateOptionString(opts []string) string {
optStr := strings.Join(opts, " ")
log.WithField("options", optStr).Debug("Generating option string")
if strings.Contains(optStr, "i2cp.leaseSetEncType") {
log.Debug("i2cp.leaseSetEncType already present in options")
return optStr
}
finalOpts := optStr + " i2cp.leaseSetEncType=4,0"
log.WithField("finalOptions", finalOpts).Debug("Added default i2cp.leaseSetEncType to options")
return finalOpts
// return optStr + " i2cp.leaseSetEncType=4,0"
}