mirror of
https://github.com/go-i2p/go-sam-go.git
synced 2025-07-04 10:16:21 -04:00
start on a wrapper for sam3
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,4 @@
|
||||
review.md
|
||||
SAMv3.md
|
||||
testplan.md
|
||||
err
|
10
SAMConn.go
Normal file
10
SAMConn.go
Normal file
@ -0,0 +1,10 @@
|
||||
package sam3
|
||||
|
||||
import (
|
||||
"github.com/go-i2p/go-sam-go/stream"
|
||||
)
|
||||
|
||||
// Implements net.Conn
|
||||
type SAMConn struct {
|
||||
*stream.StreamConn
|
||||
}
|
132
common/emitter_test.go
Normal file
132
common/emitter_test.go
Normal file
@ -0,0 +1,132 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
from = RandPort()
|
||||
to = RandPort()
|
||||
)
|
||||
|
||||
func setupTestEmit() *SAMEmit {
|
||||
return &SAMEmit{
|
||||
I2PConfig: I2PConfig{
|
||||
SamHost: "127.0.0.1",
|
||||
SamPort: 7656,
|
||||
SamMin: "3.0",
|
||||
SamMax: "3.1",
|
||||
Style: "STREAM",
|
||||
TunName: "testid",
|
||||
Fromport: from,
|
||||
Toport: to,
|
||||
SigType: "ED25519",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestSAMEmit_Hello(t *testing.T) {
|
||||
emit := setupTestEmit()
|
||||
want := "HELLO VERSION MIN=3.0 MAX=3.1 \n"
|
||||
|
||||
t.Run("string output", func(t *testing.T) {
|
||||
if got := emit.Hello(); got != want {
|
||||
t.Errorf("Hello() = %v, want %v", got, want)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("byte output", func(t *testing.T) {
|
||||
if got := emit.HelloBytes(); !bytes.Equal(got, []byte(want)) {
|
||||
t.Errorf("HelloBytes() = %v, want %v", got, []byte(want))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestSAMEmit_GenerateDestination(t *testing.T) {
|
||||
emit := setupTestEmit()
|
||||
want := "DEST GENERATE ED25519 \n"
|
||||
|
||||
t.Run("string output", func(t *testing.T) {
|
||||
if got := emit.GenerateDestination(); got != want {
|
||||
t.Errorf("GenerateDestination() = %v, want %v", got, want)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("byte output", func(t *testing.T) {
|
||||
if got := emit.GenerateDestinationBytes(); !bytes.Equal(got, []byte(want)) {
|
||||
t.Errorf("GenerateDestinationBytes() = %v, want %v", got, []byte(want))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestSAMEmit_Lookup(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
lookup string
|
||||
want string
|
||||
}{
|
||||
{"basic lookup", "test.i2p", "NAMING LOOKUP NAME=test.i2p \n"},
|
||||
{"empty lookup", "", "NAMING LOOKUP NAME= \n"},
|
||||
}
|
||||
|
||||
emit := setupTestEmit()
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := emit.Lookup(tt.lookup); got != tt.want {
|
||||
t.Errorf("Lookup() = %v, want %v", got, tt.want)
|
||||
}
|
||||
if got := emit.LookupBytes(tt.lookup); !bytes.Equal(got, []byte(tt.want)) {
|
||||
t.Errorf("LookupBytes() = %v, want %v", got, []byte(tt.want))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSAMEmit_Connect(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dest string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
"basic connect",
|
||||
"destination123",
|
||||
"STREAM CONNECT ID=testid " + from + " " + to + " DESTINATION=destination123 \n",
|
||||
},
|
||||
{
|
||||
"empty destination",
|
||||
"",
|
||||
"STREAM CONNECT ID=testid " + from + " " + to + " DESTINATION= \n",
|
||||
},
|
||||
}
|
||||
|
||||
emit := setupTestEmit()
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := emit.Connect(tt.dest); got != tt.want {
|
||||
t.Errorf("Connect() = %v, want %v", got, tt.want)
|
||||
}
|
||||
if got := emit.ConnectBytes(tt.dest); !bytes.Equal(got, []byte(tt.want)) {
|
||||
t.Errorf("ConnectBytes() = %v, want %v", got, []byte(tt.want))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSAMEmit_Accept(t *testing.T) {
|
||||
emit := setupTestEmit()
|
||||
want := "STREAM ACCEPT ID=testid " + from + " " + to + ""
|
||||
|
||||
t.Run("string output", func(t *testing.T) {
|
||||
if got := emit.Accept(); got != want {
|
||||
t.Errorf("Accept() = %v, want %v", got, want)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("byte output", func(t *testing.T) {
|
||||
if got := emit.AcceptBytes(); !bytes.Equal(got, []byte(want)) {
|
||||
t.Errorf("AcceptBytes() = %v, want %v", got, []byte(want))
|
||||
}
|
||||
})
|
||||
}
|
@ -71,8 +71,10 @@ func ExtractDest(input string) string {
|
||||
return strings.Split(input, " ")[0]
|
||||
}
|
||||
|
||||
var randSource = rand.NewSource(time.Now().UnixNano())
|
||||
var randGen = rand.New(randSource)
|
||||
var (
|
||||
randSource = rand.NewSource(time.Now().UnixNano())
|
||||
randGen = rand.New(randSource)
|
||||
)
|
||||
|
||||
func RandPort() string {
|
||||
for {
|
||||
|
62
config.go
Normal file
62
config.go
Normal file
@ -0,0 +1,62 @@
|
||||
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 = "3.0"
|
||||
config.SamMax = "3.2"
|
||||
config.TunName = ""
|
||||
config.TunType = "server"
|
||||
config.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
|
||||
}
|
13
datagram.go
Normal file
13
datagram.go
Normal file
@ -0,0 +1,13 @@
|
||||
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
|
||||
}
|
182
datagram_test.go
Normal file
182
datagram_test.go
Normal file
@ -0,0 +1,182 @@
|
||||
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!
|
||||
}
|
436
emit-options.go
Normal file
436
emit-options.go
Normal file
@ -0,0 +1,436 @@
|
||||
package sam3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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
|
||||
}
|
||||
}
|
141
emit.go
Normal file
141
emit.go
Normal file
@ -0,0 +1,141 @@
|
||||
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
|
||||
}
|
51
log.go
Normal file
51
log.go
Normal file
@ -0,0 +1,51 @@
|
||||
package sam3
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
log *logrus.Logger
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
func InitializeSAM3Logger() {
|
||||
once.Do(func() {
|
||||
log = logrus.New()
|
||||
// We do not want to log by default
|
||||
log.SetOutput(ioutil.Discard)
|
||||
log.SetLevel(logrus.PanicLevel)
|
||||
// Check if DEBUG_I2P is set
|
||||
if logLevel := os.Getenv("DEBUG_I2P"); logLevel != "" {
|
||||
log.SetOutput(os.Stdout)
|
||||
switch strings.ToLower(logLevel) {
|
||||
case "debug":
|
||||
log.SetLevel(logrus.DebugLevel)
|
||||
case "warn":
|
||||
log.SetLevel(logrus.WarnLevel)
|
||||
case "error":
|
||||
log.SetLevel(logrus.ErrorLevel)
|
||||
default:
|
||||
log.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
log.WithField("level", log.GetLevel()).Debug("Logging enabled.")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// GetSAM3Logger returns the initialized logger
|
||||
func GetSAM3Logger() *logrus.Logger {
|
||||
if log == nil {
|
||||
InitializeSAM3Logger()
|
||||
}
|
||||
return log
|
||||
}
|
||||
|
||||
func init() {
|
||||
InitializeSAM3Logger()
|
||||
}
|
40
primary.go
Normal file
40
primary.go
Normal file
@ -0,0 +1,40 @@
|
||||
package sam3
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-i2p/go-sam-go/primary"
|
||||
)
|
||||
|
||||
const (
|
||||
session_ADDOK = "SESSION STATUS RESULT=OK"
|
||||
)
|
||||
|
||||
func randport() string {
|
||||
s := rand.NewSource(time.Now().UnixNano())
|
||||
r := rand.New(s)
|
||||
p := r.Intn(55534) + 10000
|
||||
port := strconv.Itoa(p)
|
||||
log.WithField("port", port).Debug("Generated random port")
|
||||
return strconv.Itoa(p)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
@ -1,6 +1,17 @@
|
||||
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) {
|
||||
@ -8,21 +19,31 @@ func (sam *SAM) NewPrimarySession(id string, keys i2pkeys.I2PKeys, options []str
|
||||
return sam.newPrimarySession(PrimarySessionSwitch, id, keys, options)
|
||||
}
|
||||
|
||||
func (sam *SAM) newPrimarySession(primarySessionSwitch string, id string, keys i2pkeys.I2PKeys, options []string) (*PrimarySession, error) {
|
||||
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, []string{})
|
||||
conn, err := sam.NewGenericSession(primarySessionSwitch, id, keys, options)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create new generic session")
|
||||
return nil, err
|
||||
}
|
||||
ssesss := make(map[string]*StreamSession)
|
||||
dsesss := make(map[string]*DatagramSession)
|
||||
return &PrimarySession{sam.Config.I2PConfig.Sam(), id, conn, keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, sam.Config, ssesss, dsesss}, nil
|
||||
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
|
||||
@ -34,13 +55,22 @@ func (sam *SAM) NewPrimarySessionWithSignature(id string, keys i2pkeys.I2PKeys,
|
||||
"sigType": sigType,
|
||||
}).Debug("NewPrimarySessionWithSignature() called")
|
||||
|
||||
conn, err := sam.newGenericSessionWithSignature(PrimarySessionSwitch, id, keys, sigType, options, []string{})
|
||||
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
|
||||
}
|
||||
ssesss := make(map[string]*stream.StreamSession)
|
||||
dsesss := make(map[string]*datagram.DatagramSession)
|
||||
return &PrimarySession{sam.Config.I2PConfig.Sam(), id, conn, keys, time.Duration(600 * time.Second), time.Now(), sigType, sam.Config, ssesss, dsesss}, nil
|
||||
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
|
||||
}
|
||||
*/
|
||||
|
148
primary_datagram_test.go
Normal file
148
primary_datagram_test.go
Normal file
@ -0,0 +1,148 @@
|
||||
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!
|
||||
}
|
306
primary_stream_test.go
Normal file
306
primary_stream_test.go
Normal file
@ -0,0 +1,306 @@
|
||||
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!"))
|
||||
}
|
15
raw.go
Normal file
15
raw.go
Normal file
@ -0,0 +1,15 @@
|
||||
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
|
||||
}
|
24
resolver.go
Normal file
24
resolver.go
Normal file
@ -0,0 +1,24 @@
|
||||
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
|
||||
}
|
101
sam3.go
Normal file
101
sam3.go
Normal file
@ -0,0 +1,101 @@
|
||||
// Library for I2Ps SAMv3 bridge (https://geti2p.com)
|
||||
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"
|
||||
)
|
||||
|
||||
func init() {
|
||||
InitializeSAM3Logger()
|
||||
}
|
||||
|
||||
// Used for controlling I2Ps SAMv3.
|
||||
type SAM struct {
|
||||
*common.SAM
|
||||
}
|
||||
|
||||
// Creates a new stream session by wrapping stream.NewStreamSession
|
||||
func (s *SAM) NewStreamSession(param1 string, keys i2pkeys.I2PKeys, param3 []string) (*StreamSession, error) {
|
||||
sam := stream.SAM(*s.SAM)
|
||||
ss, err := sam.NewStreamSession(param1, keys, param3)
|
||||
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
|
||||
}
|
||||
|
||||
const (
|
||||
session_OK = "SESSION STATUS RESULT=OK DESTINATION="
|
||||
session_DUPLICATE_ID = "SESSION STATUS RESULT=DUPLICATED_ID\n"
|
||||
session_DUPLICATE_DEST = "SESSION STATUS RESULT=DUPLICATED_DEST\n"
|
||||
session_INVALID_KEY = "SESSION STATUS RESULT=INVALID_KEY\n"
|
||||
session_I2P_ERROR = "SESSION STATUS RESULT=I2P_ERROR MESSAGE="
|
||||
)
|
||||
|
||||
const (
|
||||
Sig_NONE = "SIGNATURE_TYPE=EdDSA_SHA512_Ed25519"
|
||||
Sig_DSA_SHA1 = "SIGNATURE_TYPE=DSA_SHA1"
|
||||
Sig_ECDSA_SHA256_P256 = "SIGNATURE_TYPE=ECDSA_SHA256_P256"
|
||||
Sig_ECDSA_SHA384_P384 = "SIGNATURE_TYPE=ECDSA_SHA384_P384"
|
||||
Sig_ECDSA_SHA512_P521 = "SIGNATURE_TYPE=ECDSA_SHA512_P521"
|
||||
Sig_EdDSA_SHA512_Ed25519 = "SIGNATURE_TYPE=EdDSA_SHA512_Ed25519"
|
||||
)
|
||||
|
||||
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
|
||||
func RandString() string {
|
||||
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
|
||||
}
|
164
sam3_test.go
Normal file
164
sam3_test.go
Normal file
@ -0,0 +1,164 @@
|
||||
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())
|
||||
}
|
29
stream.go
Normal file
29
stream.go
Normal file
@ -0,0 +1,29 @@
|
||||
package sam3
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"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
|
||||
}*/
|
||||
|
||||
func minNonzeroTime(a, b time.Time) time.Time {
|
||||
if a.IsZero() {
|
||||
return b
|
||||
}
|
||||
if b.IsZero() || a.Before(b) {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
9
streamListener.go
Normal file
9
streamListener.go
Normal file
@ -0,0 +1,9 @@
|
||||
package sam3
|
||||
|
||||
import (
|
||||
"github.com/go-i2p/go-sam-go/stream"
|
||||
)
|
||||
|
||||
type StreamListener struct {
|
||||
*stream.StreamListener
|
||||
}
|
283
stream_test.go
Normal file
283
stream_test.go
Normal file
@ -0,0 +1,283 @@
|
||||
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!
|
||||
}
|
120
suggestedOptions.go
Normal file
120
suggestedOptions.go
Normal file
@ -0,0 +1,120 @@
|
||||
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 {
|
||||
InitializeSAM3Logger()
|
||||
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"
|
||||
}
|
Reference in New Issue
Block a user