mirror of
https://github.com/go-i2p/go-sam-go.git
synced 2025-07-05 02:23:50 -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
|
review.md
|
||||||
SAMv3.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]
|
return strings.Split(input, " ")[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
var randSource = rand.NewSource(time.Now().UnixNano())
|
var (
|
||||||
var randGen = rand.New(randSource)
|
randSource = rand.NewSource(time.Now().UnixNano())
|
||||||
|
randGen = rand.New(randSource)
|
||||||
|
)
|
||||||
|
|
||||||
func RandPort() string {
|
func RandPort() string {
|
||||||
for {
|
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
|
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
|
// Creates a new PrimarySession with the I2CP- and streaminglib options as
|
||||||
// specified. See the I2P documentation for a full list of options.
|
// specified. See the I2P documentation for a full list of options.
|
||||||
func (sam *SAM) NewPrimarySession(id string, keys i2pkeys.I2PKeys, options []string) (*PrimarySession, error) {
|
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)
|
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{
|
log.WithFields(logrus.Fields{
|
||||||
"primarySessionSwitch": primarySessionSwitch,
|
"primarySessionSwitch": primarySessionSwitch,
|
||||||
"id": id,
|
"id": id,
|
||||||
"options": options,
|
"options": options,
|
||||||
}).Debug("newPrimarySession() called")
|
}).Debug("newPrimarySession() called")
|
||||||
|
|
||||||
conn, err := sam.newGenericSession(primarySessionSwitch, id, keys, options, []string{})
|
conn, err := sam.NewGenericSession(primarySessionSwitch, id, keys, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("Failed to create new generic session")
|
log.WithError(err).Error("Failed to create new generic session")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ssesss := make(map[string]*StreamSession)
|
return &PrimarySession{
|
||||||
dsesss := make(map[string]*DatagramSession)
|
SAM: sam,
|
||||||
return &PrimarySession{sam.Config.I2PConfig.Sam(), id, conn, keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, sam.Config, ssesss, dsesss}, nil
|
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
|
// 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,
|
"sigType": sigType,
|
||||||
}).Debug("NewPrimarySessionWithSignature() called")
|
}).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 {
|
if err != nil {
|
||||||
log.WithError(err).Error("Failed to create new generic session with signature")
|
log.WithError(err).Error("Failed to create new generic session with signature")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ssesss := make(map[string]*stream.StreamSession)
|
return &PrimarySession{
|
||||||
dsesss := make(map[string]*datagram.DatagramSession)
|
SAM: sam,
|
||||||
return &PrimarySession{sam.Config.I2PConfig.Sam(), id, conn, keys, time.Duration(600 * time.Second), time.Now(), sigType, sam.Config, ssesss, dsesss}, nil
|
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