19 Commits

Author SHA1 Message Date
idk
964219c25f Disable datagram support in the dialer function until it's ready, so I can cut a release tonight. 2022-02-01 21:50:35 -05:00
idk
23c45022b3 tests should run in debug mode. Don't attempt to hot-restore SAM sessions by replacing the net.Conn parts inside if they go down, just let it die and tell the user to restart. 2022-02-01 20:03:13 -05:00
idk
c6d9c0e340 protect the dialer with a mutex 2021-04-15 19:16:11 -04:00
idk
460926afe8 Merge pull request #7 from eyedeekay/no-keys
Don't rely on sam3 for key generation. Cleanup API issues. Improve Stability. Begin to add DG, RAW support.
2021-04-15 14:24:56 -07:00
idk
e278de3a66 Fix a bunch of errors that could potentially happen because of changes I made to the Dialer 2021-04-15 17:21:41 -04:00
idk
d1d2663c42 Add session creation commands for datagrams and raw sessions, stub out a repliable datagram dialer, add a DatagramConn interface that implements both net.Conn and net.PacketConn so that Dial can return a whole DatagramConn 2021-02-24 23:08:19 -05:00
idk
5af3086205 Add session creation commands for datagrams and raw sessions, stub out a repliable datagram dialer, add a DatagramConn interface that implements both net.Conn and net.PacketConn so that Dial can return a whole DatagramConn 2021-02-24 23:04:55 -05:00
idk
f97683379f make a version which stringifies i2pkeys 2021-01-22 16:18:17 -05:00
idk
42d542dd8b make a version which stringifies i2pkeys 2021-01-22 16:18:13 -05:00
idk
d94d9c4da0 make a version which stringifies i2pkeys 2021-01-22 16:17:35 -05:00
idk
dddd8ea916 make a version which stringifies i2pkeys 2021-01-22 16:10:24 -05:00
idk
b864407cc2 Allow clients to force a handshake at a specific SAM version or between SAM versions 2020-12-07 01:47:33 -05:00
idk
646767e7bf Allow clients to force a handshake at a specific SAM version or between SAM versions 2020-12-07 00:51:51 -05:00
idk
76e1e9af83 Dial/Hangup Hack. If the connection is closed because of an error, re-dial it from within the dial function itself by incrementing the ID and creating a new session on top of the old one. 2020-12-05 16:36:19 -05:00
idk
6bd37c4d51 kill connections when they die in context 2020-11-29 16:53:46 -05:00
idk
e2f73efb10 Improve the parser, automatically set empty destinations to TRANSIENT when using a dialer, better error handling, when a socket gets closed, increment the ID and re-create it 2020-11-29 16:30:51 -05:00
idk
5fda56e88d Improve the parser, automatically set empty destinations to TRANSIENT when using a dialer, better error handling, when a socket gets closed, increment the ID and re-create it 2020-11-29 16:23:55 -05:00
idk
63833f24ab Improve the parser, automatically set empty destinations to TRANSIENT when using a dialer, better error handling, when a socket gets closed, increment the ID and re-create it 2020-11-29 16:12:35 -05:00
idk
0d10b5b516 Improve the parser, automatically set empty destinations to TRANSIENT when using a dialer, better error handling, when a socket gets closed, increment the ID and re-create it 2020-11-29 16:09:55 -05:00
19 changed files with 314 additions and 178 deletions

View File

@@ -1,6 +1,6 @@
USER_GH=eyedeekay
VERSION=0.32.29
VERSION=0.32.4
packagename=gosam
echo: fmt

View File

@@ -24,20 +24,12 @@ func (c *Client) Listen() (net.Listener, error) {
// with Accept
func (c *Client) ListenI2P(dest string) (net.Listener, error) {
var err error
c.id = c.NewID()
c.destination, err = c.CreateStreamSession(c.id, dest)
d := c.destination
c.destination, err = c.CreateStreamSession(dest)
if err != nil {
return nil, err
}
fmt.Println("Listening on destination:", c.Base32()+".b32.i2p")
c, err = c.NewClient()
if err != nil {
return nil, err
}
c.destination = d
if c.debug {
c.SamConn = WrapConn(c.SamConn)
}
@@ -52,7 +44,7 @@ func (c *Client) Accept() (net.Conn, error) {
if c.id == 0 {
return c.AcceptI2P()
}
resp, err := c.StreamAccept(c.id)
resp, err := c.StreamAccept()
if err != nil {
return nil, err
}

View File

@@ -12,6 +12,9 @@ import (
"net"
"strings"
"sync"
"time"
samkeys "github.com/eyedeekay/goSam/compat"
)
// A Client represents a single Connection to the SAM bridge
@@ -21,8 +24,9 @@ type Client struct {
fromport string
toport string
SamConn net.Conn
rd *bufio.Reader
SamConn net.Conn
SamDGConn DatagramConn
rd *bufio.Reader
sigType string
destination string
@@ -51,13 +55,14 @@ type Client struct {
compression bool
debug bool
mutex sync.Mutex
//NEVER, EVER modify lastaddr or id yourself. They are used internally only.
lastaddr string
id int32
ml sync.Mutex
oml sync.Mutex
id int32
sammin int
sammax int
}
// SAMsigTypes is a slice of the available signature types
var SAMsigTypes = []string{
"SIGNATURE_TYPE=DSA_SHA1",
"SIGNATURE_TYPE=ECDSA_SHA256_P256",
@@ -66,6 +71,12 @@ var SAMsigTypes = []string{
"SIGNATURE_TYPE=EdDSA_SHA512_Ed25519",
}
var ValidSAMCommands = []string{
"HELLO",
"SESSION",
"STREAM",
}
var (
i2pB64enc *base64.Encoding = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~")
i2pB32enc *base32.Encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
@@ -83,7 +94,11 @@ func NewClient(addr string) (*Client, error) {
// NewID generates a random number to use as an tunnel name
func (c *Client) NewID() int32 {
return rand.Int31n(math.MaxInt32)
if c.id == 0 {
c.id = rand.Int31n(math.MaxInt32)
fmt.Printf("Initializing new ID: %d\n", c.id)
}
return c.id
}
// Destination returns the full destination of the local tunnel
@@ -127,11 +142,11 @@ func NewClientFromOptions(opts ...func(*Client) error) (*Client, error) {
c.port = "7656"
c.inLength = 3
c.inVariance = 0
c.inQuantity = 1
c.inQuantity = 3
c.inBackups = 1
c.outLength = 3
c.outVariance = 0
c.outQuantity = 1
c.outQuantity = 3
c.outBackups = 1
c.dontPublishLease = true
c.encryptLease = false
@@ -140,20 +155,22 @@ func NewClientFromOptions(opts ...func(*Client) error) (*Client, error) {
c.reduceIdleQuantity = 1
c.closeIdle = true
c.closeIdleTime = 600000
c.debug = true
c.debug = false
c.sigType = SAMsigTypes[4]
c.id = 0
c.lastaddr = "invalid"
c.destination = ""
c.leaseSetEncType = "4,0"
c.fromport = ""
c.toport = ""
c.sammin = 0
c.sammax = 1
for _, o := range opts {
if err := o(&c); err != nil {
return nil, err
}
}
conn, err := net.Dial("tcp", c.samaddr())
c.id = c.NewID()
conn, err := net.DialTimeout("tcp", c.samaddr(), 15*time.Minute)
if err != nil {
return nil, err
}
@@ -165,12 +182,22 @@ func NewClientFromOptions(opts ...func(*Client) error) (*Client, error) {
return &c, c.hello()
}
// ID returns a the current ID of the client as a string
func (p *Client) ID() string {
return fmt.Sprintf("%d", p.id)
return fmt.Sprintf("%d", p.NewID())
}
// Addr returns the address of the client as a net.Addr
func (p *Client) Addr() net.Addr {
return nil
keys, err := samkeys.DestToKeys(p.Destination())
if err != nil {
return nil
}
return keys.Addr()
}
func (p *Client) LocalAddr() net.Addr {
return p.Addr()
}
//return the combined host:port of the SAM bridge
@@ -180,7 +207,7 @@ func (c *Client) samaddr() string {
// send the initial handshake command and check that the reply is ok
func (c *Client) hello() error {
r, err := c.sendCmd("HELLO VERSION MIN=3.0 MAX=3.2\n")
r, err := c.sendCmd("HELLO VERSION MIN=3.%d MAX=3.%d\n", c.sammin, c.sammax)
if err != nil {
return err
}
@@ -216,8 +243,10 @@ func (c *Client) Close() error {
return c.SamConn.Close()
}
// NewClient generates an exact copy of the client with the same options
func (c *Client) NewClient() (*Client, error) {
// NewClient generates an exact copy of the client with the same options, but
// re-does all the handshaky business so that Dial can pick up right where it
// left off, should the need arise.
func (c *Client) NewClient(id int32) (*Client, error) {
return NewClientFromOptions(
SetHost(c.host),
SetPort(c.port),
@@ -238,7 +267,6 @@ func (c *Client) NewClient() (*Client, error) {
SetCloseIdle(c.closeIdle),
SetCloseIdleTime(c.closeIdleTime),
SetCompression(c.compression),
setlastaddr(c.lastaddr),
setid(c.id),
setid(id),
)
}

View File

@@ -1,3 +1,4 @@
//go:build nettest
// +build nettest
package goSam
@@ -6,6 +7,8 @@ import "testing"
import (
"fmt"
"math"
"math/rand"
"time"
//"log"
"net/http"
@@ -18,48 +21,40 @@ func HelloServer(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}
var client *Client
func setup(t *testing.T) {
var err error
// these tests expect a running SAM brige on this address
client, err = NewClientFromOptions(SetDebug(true))
if err != nil {
t.Fatalf("NewDefaultClient() Error: %q\n", err)
}
}
func TestCompositeClient(t *testing.T) {
listener, err := sam.I2PListener("testservice", "127.0.0.1:7656", "testkeys")
listener, err := sam.I2PListener("testservice"+fmt.Sprintf("%d", rand.Int31n(math.MaxInt32)), "127.0.0.1:7656", "testkeys")
if err != nil {
t.Fatalf("Listener() Error: %q\n", err)
}
defer listener.Close()
http.HandleFunc("/", HelloServer)
go http.Serve(listener, nil)
listener2, err := sam.I2PListener("testservice2", "127.0.0.1:7656", "testkeys2")
listener2, err := sam.I2PListener("testservice"+fmt.Sprintf("%d", rand.Int31n(math.MaxInt32)), "127.0.0.1:7656", "testkeys2")
if err != nil {
t.Fatalf("Listener() Error: %q\n", err)
}
defer listener2.Close()
// http.HandleFunc("/", HelloServer)
go http.Serve(listener2, nil)
listener3, err := sam.I2PListener("testservice3", "127.0.0.1:7656", "testkeys3")
listener3, err := sam.I2PListener("testservice"+fmt.Sprintf("%d", rand.Int31n(math.MaxInt32)), "127.0.0.1:7656", "testkeys3")
if err != nil {
t.Fatalf("Listener() Error: %q\n", err)
}
defer listener3.Close()
// http.HandleFunc("/", HelloServer)
go http.Serve(listener3, nil)
client, err = NewClientFromOptions(SetDebug(true))
sam, err := NewClientFromOptions(SetDebug(true))
if err != nil {
t.Fatalf("NewDefaultClient() Error: %q\n", err)
}
tr := &http.Transport{
Dial: client.Dial,
Dial: sam.Dial,
}
client := &http.Client{Transport: tr}
defer sam.Close()
time.Sleep(time.Second * 30)
go func() {
resp, err := client.Get("http://" + listener.Addr().(i2pkeys.I2PAddr).Base32())
@@ -67,45 +62,50 @@ func TestCompositeClient(t *testing.T) {
t.Fatalf("Get Error: %q\n", err)
}
defer resp.Body.Close()
t.Log("Get returned ", resp)
}()
time.Sleep(time.Second * 15)
go func() {
resp, err := client.Get("http://" + listener2.Addr().(i2pkeys.I2PAddr).Base32())
if err != nil {
t.Fatalf("Get Error: %q\n", err)
}
defer resp.Body.Close()
t.Log("Get returned ", resp)
}()
time.Sleep(time.Second * 15)
go func() {
resp, err := client.Get("http://" + listener3.Addr().(i2pkeys.I2PAddr).Base32())
if err != nil {
t.Fatalf("Get Error: %q\n", err)
}
defer resp.Body.Close()
t.Log("Get returned ", resp)
}()
time.Sleep(time.Second * 15)
time.Sleep(time.Second * 45)
}
func teardown(t *testing.T) {
func TestClientHello(t *testing.T) {
client, err := NewClientFromOptions(SetDebug(true))
if err != nil {
t.Fatalf("NewDefaultClient() Error: %q\n", err)
}
t.Log(client.Base32())
if err := client.Close(); err != nil {
t.Fatalf("client.Close() Error: %q\n", err)
}
}
func TestClientHello(t *testing.T) {
setup(t)
t.Log(client.Base32())
teardown(t)
}
func TestNewDestination(t *testing.T) {
setup(t)
t.Log(client.Base32())
if _, err := client.NewDestination(SAMsigTypes[3]); err != nil {
t.Error(err)
client, err := NewClientFromOptions(SetDebug(true))
if err != nil {
t.Fatalf("NewDefaultClient() Error: %q\n", err)
}
t.Log(client.Base32())
if s, p, err := client.NewDestination(SAMsigTypes[3]); err != nil {
t.Error(err)
} else {
t.Log(s, p)
}
if err := client.Close(); err != nil {
t.Fatalf("client.Close() Error: %q\n", err)
}
teardown(t)
}

View File

@@ -30,11 +30,14 @@ import (
"time"
)
// Conn Read data from the connection, writes data to te connection
// and logs the data in-between.
type Conn struct {
RWC
conn net.Conn
}
// WrapConn wraps a net.Conn in a Conn.
func WrapConn(c net.Conn) *Conn {
wrap := Conn{
conn: c,
@@ -45,23 +48,29 @@ func WrapConn(c net.Conn) *Conn {
return &wrap
}
// LocalAddr returns the local address of the connection.
func (c *Conn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
// RemoteAddr returns the remote address of the connection.
func (c *Conn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
// SetDeadline sets the read and write deadlines associated with the connection
func (c *Conn) SetDeadline(t time.Time) error {
log.Println("WARNING: SetDeadline() not sure this works")
return c.conn.SetDeadline(t)
}
// SetReadDeadline sets the read deadline associated with the connection
func (c *Conn) SetReadDeadline(t time.Time) error {
log.Println("WARNING: SetReadDeadline() not sure this works")
return c.conn.SetReadDeadline(t)
}
// SetWriteDeadline sets the write deadline associated with the connection
func (c *Conn) SetWriteDeadline(t time.Time) error {
log.Println("WARNING: SetWriteDeadline() not sure this works")
return c.conn.SetWriteDeadline(t)

26
datagram.go Normal file
View File

@@ -0,0 +1,26 @@
package goSam
import (
"net"
"time"
)
// DatagramConn
type DatagramConn interface {
ReadFrom(p []byte) (n int, addr net.Addr, err error)
Read(b []byte) (n int, err error)
WriteTo(p []byte, addr net.Addr) (n int, err error)
Write(b []byte) (n int, err error)
Close() error
LocalAddr() net.Addr
RemoteAddr() net.Addr
SetDeadline(t time.Time) error
SetReadDeadline(t time.Time) error
SetWriteDeadline(t time.Time) error
}
/**
var conn DatagramConn = &Client{}
*/

13
debian/changelog vendored
View File

@@ -1,10 +1,19 @@
golang-github-eyedeekay-gosam (0.32.30) UNRELEASED; urgency=medium
* Improve the defaults
* Handle errors more correctly
* Gracefully handle hangups by creating a new session
* Set empty destinations to TRANSIENT when dialing
-- idk <hankhill19580@gmail.com> Sun, 03 Nov 2020 16:12:04 -0500
golang-github-eyedeekay-gosam (0.32.29) UNRELEASED; urgency=medium
* Maintenance updates
-- idk <hankhill19580@gmail.com> Mon, 23 Nov 2020 20:40:4 -0500
-- idk <hankhill19580@gmail.com> Mon, 23 Nov 2020 20:40:40 -0500
golang-github-eyedeekay-gosam (0.32.27) UNRELEASED; urgency=medium
golang-github-eyedeekay-gosam (0.32.28) UNRELEASED; urgency=medium
* Maintenance updates

65
dial.go
View File

@@ -3,78 +3,81 @@ package goSam
import (
"context"
"fmt"
"log"
"net"
"strings"
)
// DialContext implements the net.DialContext function and can be used for http.Transport
func (c *Client) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
c.oml.Lock()
defer c.oml.Unlock()
c.mutex.Lock()
defer c.mutex.Unlock()
errCh := make(chan error, 1)
connCh := make(chan net.Conn, 1)
go func() {
if conn, err := c.Dial(network, addr); err != nil {
if conn, err := c.DialContextFree(network, addr); err != nil {
errCh <- err
} else if ctx.Err() != nil {
var err error
c, err = c.NewClient()
if err != nil {
conn.Close()
}
log.Println(ctx)
errCh <- ctx.Err()
} else {
connCh <- conn
}
}()
select {
case err := <-errCh:
// var err error
c, err = c.NewClient()
return c.SamConn, err
return nil, err
case conn := <-connCh:
return conn, nil
case <-ctx.Done():
return nil, ctx.Err()
}
}
func (c *Client) dialCheck(addr string) (int32, bool) {
if c.lastaddr == "invalid" {
fmt.Println("Preparing to dial new address.")
return c.NewID(), true
} else if c.lastaddr != addr {
fmt.Println("Preparing to dial next new address.")
}
return c.id, false
func (c *Client) Dial(network, addr string) (net.Conn, error) {
return c.DialContext(context.TODO(), network, addr)
}
// Dial implements the net.Dial function and can be used for http.Transport
func (c *Client) Dial(network, addr string) (net.Conn, error) {
c.ml.Lock()
defer c.ml.Unlock()
func (c *Client) DialContextFree(network, addr string) (net.Conn, error) {
if network == "tcp" || network == "tcp6" || network == "tcp4" {
return c.DialStreamingContextFree(addr)
}
if network == "udp" || network == "udp6" || network == "udp4" {
return c.DialDatagramContextFree(addr)
}
if network == "raw" || network == "ip" {
return c.DialDatagramContextFree(addr)
}
return c.DialStreamingContextFree(addr)
}
// DialDatagramContextFree is a "Dialer" for "Client-Like" Datagram connections.
// It is also not finished. If you need datagram support right now, use sam3.
func (c *Client) DialDatagramContextFree(addr string) (DatagramConn, error) {
return nil, fmt.Errorf("Datagram support is not finished yet, come back later`")
}
func (c *Client) DialStreamingContextFree(addr string) (net.Conn, error) {
portIdx := strings.Index(addr, ":")
if portIdx >= 0 {
addr = addr[:portIdx]
}
addr, err := c.Lookup(addr)
if err != nil {
log.Printf("LOOKUP DIALER ERROR %s %s", addr, err)
return nil, err
}
var test bool
if c.id, test = c.dialCheck(addr); test == true {
c.destination, err = c.CreateStreamSession(c.id, c.destination)
if c.destination == "" {
c.destination, err = c.CreateStreamSession(c.destination)
if err != nil {
return nil, err
}
c.lastaddr = addr
}
c, err = c.NewClient()
if err != nil {
return nil, err
}
err = c.StreamConnect(c.id, addr)
err = c.StreamConnect(addr)
if err != nil {
return nil, err
}

3
go.mod
View File

@@ -1,10 +1,11 @@
module github.com/eyedeekay/goSam
require (
github.com/eyedeekay/sam3 v0.32.32-0.20201122050855-f464873c9350
github.com/eyedeekay/sam3 v0.32.32
github.com/getlantern/go-socks5 v0.0.0-20171114193258-79d4dd3e2db5
github.com/getlantern/golog v0.0.0-20201105130739-9586b8bde3a9 // indirect
github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd // indirect
github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6 // indirect
)
//replace github.com/eyedeekay/gosam v0.1.1-0.20190814195658-27e786578944 => github.com/eyedeekay/goSam ./

4
go.sum
View File

@@ -6,6 +6,8 @@ github.com/eyedeekay/sam3 v0.32.2/go.mod h1:Y3igFVzN4ybqkkpfUWULGhw7WRp8lieq0ORX
github.com/eyedeekay/sam3 v0.32.31 h1:0fdDAupEQZSETHcyVQAsnFgpYArGJzU+lC2qN6f0GDk=
github.com/eyedeekay/sam3 v0.32.32-0.20201122050855-f464873c9350 h1:8R4zcaWsgANiZ4MKKBPUf9Isct2M1IFVUVZdAMqPCmU=
github.com/eyedeekay/sam3 v0.32.32-0.20201122050855-f464873c9350/go.mod h1:qRA9KIIVxbrHlkj+ZB+OoxFGFgdKeGp1vSgPw26eOVU=
github.com/eyedeekay/sam3 v0.32.32 h1:9Ea1Ere5O8Clx8zYxKnvhrWy7R96Q4FvxlPskYf8VW0=
github.com/eyedeekay/sam3 v0.32.32/go.mod h1:qRA9KIIVxbrHlkj+ZB+OoxFGFgdKeGp1vSgPw26eOVU=
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4=
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
github.com/getlantern/errors v1.0.1 h1:XukU2whlh7OdpxnkXhNH9VTLVz0EVPGKDV5K0oWhvzw=
@@ -22,6 +24,8 @@ github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd h1:mn98vs69Kqw56iK
github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd/go.mod h1:wKdY0ikOgzrWSeB9UyBVKPRhjXQ+vTb+BPeJuypUuNE=
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA=
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6 h1:QthAQCekS1YOeYWSvoHI6ZatlG4B+GBDLxV/2ZkBsTA=
github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=

View File

@@ -2,17 +2,14 @@ package goSam
import (
"errors"
"github.com/eyedeekay/sam3/i2pkeys"
)
// NewDestination generates a new I2P destination, creating the underlying
// public/private keys in the process. The public key can be used to send messages
// to the destination, while the private key can be used to reply to messages
func (c *Client) NewDestination(sigType ...string) (i2pkeys.I2PKeys, error) {
func (c *Client) NewDestination(sigType ...string) (string, string, error) {
var (
sigtmp string
keys i2pkeys.I2PKeys
)
if len(sigType) > 0 {
sigtmp = sigType[0]
@@ -22,14 +19,14 @@ func (c *Client) NewDestination(sigType ...string) (i2pkeys.I2PKeys, error) {
sigtmp,
)
if err != nil {
return keys, err
return "", "", err
}
var pub, priv string
if priv = r.Pairs["PRIV"]; priv == "" {
return keys, errors.New("failed to generate private destination key")
return "", "", errors.New("failed to generate private destination key")
}
if pub = r.Pairs["PUB"]; pub == "" {
return keys, errors.New("failed to generate public destination key")
return priv, "", errors.New("failed to generate public destination key")
}
return i2pkeys.NewKeys(i2pkeys.I2PAddr(pub), priv), nil
return priv, pub, nil
}

View File

@@ -17,7 +17,7 @@ func (c *Client) Lookup(name string) (string, error) {
// TODO: move check into sendCmd()
if r.Topic != "NAMING" || r.Type != "REPLY" {
return "", fmt.Errorf("Naming Unknown Reply: %+v\n", r)
return "", fmt.Errorf("Naming Unknown Reply: %s, %s\n", r.Topic, r.Type)
}
result := r.Pairs["RESULT"]
@@ -37,37 +37,39 @@ func (c *Client) Lookup(name string) (string, error) {
}
func (c *Client) forward(client, conn net.Conn) {
defer client.Close()
defer conn.Close()
go func() {
defer client.Close()
defer conn.Close()
// defer client.Close()
// defer conn.Close()
io.Copy(client, conn)
}()
go func() {
defer client.Close()
defer conn.Close()
// defer client.Close()
// defer conn.Close()
io.Copy(conn, client)
}()
}
func (c *Client) Resolve(ctx context.Context, name string) (context.Context, net.IP, error) {
if c.lastaddr == "invalid" || c.lastaddr != name {
client, err := c.DialContext(ctx, "", name)
if err != nil {
return ctx, nil, err
}
ln, err := net.Listen("tcp", "127.0.0.1:")
if err != nil {
return ctx, nil, err
}
go func() {
for {
conn, err := ln.Accept()
if err != nil {
fmt.Println(err.Error())
}
go c.forward(client, conn)
}
}()
// if c.lastaddr == "invalid" || c.lastaddr != name {
client, err := c.DialContext(ctx, "", name)
if err != nil {
return ctx, nil, err
}
ln, err := net.Listen("tcp", "127.0.0.1:")
if err != nil {
return ctx, nil, err
}
go func() {
for {
conn, err := ln.Accept()
if err != nil {
fmt.Println(err.Error())
}
go c.forward(client, conn)
}
}()
// }
return ctx, nil, nil
}

View File

@@ -1,3 +1,4 @@
//go:build nettest
// +build nettest
package goSam
@@ -10,8 +11,10 @@ import (
func TestClientLookupInvalid(t *testing.T) {
var err error
setup(t)
defer teardown(t)
client, err := NewClientFromOptions(SetDebug(true))
if err != nil {
t.Fatalf("NewDefaultClient() Error: %q\n", err)
}
addr, err := client.Lookup(`!(@#)`)
if addr != "" || err == nil {
@@ -25,6 +28,9 @@ func TestClientLookupInvalid(t *testing.T) {
if repErr.Result != ResultKeyNotFound {
t.Errorf("client.Lookup() should throw an ResultKeyNotFound error.\nGot:%+v%s%s\n", repErr, "!=", ResultKeyNotFound)
}
if err := client.Close(); err != nil {
t.Fatalf("client.Close() Error: %q\n", err)
}
}
func TestClientLookupValid(t *testing.T) {

View File

@@ -62,6 +62,32 @@ func SetHost(s string) func(*Client) error {
}
}
func SetSAMMinVersion(i int) func(*Client) error {
return func(c *Client) error {
if i < 0 {
return fmt.Errorf("SAM version must be greater than or equal to 0")
}
if i > 3 {
return fmt.Errorf("SAM version must be less than or equal to 3")
}
c.sammin = i
return nil
}
}
func SetSAMMaxVersion(i int) func(*Client) error {
return func(c *Client) error {
if i < 0 {
return fmt.Errorf("SAM version must be greater than or equal to 0")
}
if i > 3 {
return fmt.Errorf("SAM version must be less than or equal to 3")
}
c.sammin = i
return nil
}
}
//SetLocalDestination sets the local destination of the tunnel from a private
//key
func SetLocalDestination(s string) func(*Client) error {
@@ -71,13 +97,6 @@ func SetLocalDestination(s string) func(*Client) error {
}
}
func setlastaddr(s string) func(*Client) error {
return func(c *Client) error {
c.lastaddr = s
return nil
}
}
func setid(s int32) func(*Client) error {
return func(c *Client) error {
c.id = s

View File

@@ -1,9 +1,8 @@
//go:build nettest
// +build nettest
package goSam
/*
import (
"fmt"
"math"
@@ -33,7 +32,7 @@ func (c *Client) validCmd(str string, args ...interface{}) (string, error) {
func (c *Client) validCreate() (string, error) {
id := rand.Int31n(math.MaxInt32)
result, err := c.validCmd("SESSION CREATE STYLE=STREAM ID=%d DESTINATION=%s %s\n", id, "abc.i2p", client.allOptions())
result, err := c.validCmd("SESSION CREATE STYLE=STREAM ID=%d DESTINATION=%s %s\n", id, "abc.i2p", c.allOptions())
return result, err
}
@@ -47,13 +46,13 @@ func TestOptionAddrString(t *testing.T) {
} else {
t.Log(result)
}
dest, _ := client.CreateStreamSession(client.NewID(), "")
_, err = client.CreateStreamSession("")
if err := client.Close(); err != nil {
t.Fatalf("client.Close() Error: %q\n", err)
}
fmt.Printf("\t destination- %s \n", dest)
fmt.Printf("\t address64- %s \t", client.Base64())
fmt.Printf("\t address- %s \t", client.Base32())
/* fmt.Printf("\t destination- %s \n", dest)
fmt.Printf("\t address64- %s \t", client.Base64())
fmt.Printf("\t address- %s \t", client.Base32())*/
}
func TestOptionAddrStringLh(t *testing.T) {
@@ -66,13 +65,13 @@ func TestOptionAddrStringLh(t *testing.T) {
} else {
t.Log(result)
}
dest, _ := client.CreateStreamSession(client.NewID(), "")
_, err = client.CreateStreamSession("")
if err := client.Close(); err != nil {
t.Fatalf("client.Close() Error: %q\n", err)
}
fmt.Printf("\t destination- %s \n", dest)
fmt.Printf("\t address64- %s \t", client.Base64())
fmt.Printf("\t address- %s \t", client.Base32())
/* fmt.Printf("\t destination- %s \n", dest)
fmt.Printf("\t address64- %s \t", client.Base64())
fmt.Printf("\t address- %s \t", client.Base32())*/
}
func TestOptionAddrSlice(t *testing.T) {
@@ -85,13 +84,13 @@ func TestOptionAddrSlice(t *testing.T) {
} else {
t.Log(result)
}
dest, _ := client.CreateStreamSession(client.NewID(), "")
_, err = client.CreateStreamSession("")
if err := client.Close(); err != nil {
t.Fatalf("client.Close() Error: %q\n", err)
}
fmt.Printf("\t destination- %s \n", dest)
fmt.Printf("\t address64- %s \t", client.Base64())
fmt.Printf("\t address- %s \t", client.Base32())
/* fmt.Printf("\t destination- %s \n", dest)
fmt.Printf("\t address64- %s \t", client.Base64())
fmt.Printf("\t address- %s \t", client.Base32())*/
}
func TestOptionAddrMixedSlice(t *testing.T) {
@@ -104,13 +103,13 @@ func TestOptionAddrMixedSlice(t *testing.T) {
} else {
t.Log(result)
}
dest, _ := client.CreateStreamSession(client.NewID(), "")
_, err = client.CreateStreamSession("")
if err := client.Close(); err != nil {
t.Fatalf("client.Close() Error: %q\n", err)
}
fmt.Printf("\t destination- %s \n", dest)
fmt.Printf("\t address64- %s \t", client.Base64())
fmt.Printf("\t address- %s \t", client.Base32())
/* fmt.Printf("\t destination- %s \n", dest)
fmt.Printf("\t address64- %s \t", client.Base64())
fmt.Printf("\t address- %s \t", client.Base32())*/
}
func TestOptionHost(t *testing.T) {
@@ -142,13 +141,13 @@ func TestOptionHost(t *testing.T) {
} else {
t.Log(result)
}
dest, _ := client.CreateStreamSession(client.NewID(), "")
_, err = client.CreateStreamSession("")
if err := client.Close(); err != nil {
t.Fatalf("client.Close() Error: %q\n", err)
}
fmt.Printf("\t destination- %s \n", dest)
fmt.Printf("\t address64- %s \t", client.Base64())
fmt.Printf("\t address- %s \t", client.Base32())
/* fmt.Printf("\t destination- %s \n", dest)
fmt.Printf("\t address64- %s \t", client.Base64())
fmt.Printf("\t address- %s \t", client.Base32())*/
}
func TestOptionPortInt(t *testing.T) {
@@ -180,12 +179,11 @@ func TestOptionPortInt(t *testing.T) {
} else {
t.Log(result)
}
dest, _ := client.CreateStreamSession(client.NewID(), "")
_, err = client.CreateStreamSession("")
if err := client.Close(); err != nil {
t.Fatalf("client.Close() Error: %q\n", err)
}
fmt.Printf("\t destination- %s \n", dest)
fmt.Printf("\t address64- %s \t", client.Base64())
fmt.Printf("\t address- %s \t", client.Base32())
/* fmt.Printf("\t destination- %s \n", dest)
fmt.Printf("\t address64- %s \t", client.Base64())
fmt.Printf("\t address- %s \t", client.Base32())*/
}
*/

1
raw.go Normal file
View File

@@ -0,0 +1 @@
package goSam

View File

@@ -44,6 +44,25 @@ func parseReply(line string) (*Reply, error) {
if len(parts) < 3 {
return nil, fmt.Errorf("Malformed Reply.\n%s\n", line)
}
preParseReply := func() []string {
val := ""
quote := false
for _, v := range parts {
if strings.Contains(v, "=\"") {
quote = true
}
if strings.Contains(v, "\"\n") || strings.Contains(v, "\" ") {
quote = false
}
if quote {
val += v + "_"
} else {
val += v + " "
}
}
return strings.Split(strings.TrimSuffix(strings.TrimSpace(val), "_"), " ")
}
parts = preParseReply()
r := &Reply{
Topic: parts[0],
@@ -63,9 +82,8 @@ func parseReply(line string) (*Reply, error) {
} else {
kvPair := strings.SplitN(v, "=", 2)
if kvPair != nil {
if len(kvPair) == 1 {
} else if len(kvPair) != 2 {
return nil, fmt.Errorf("Malformed key-value-pair.\n%s\n", kvPair)
if len(kvPair) != 2 {
return nil, fmt.Errorf("Malformed key-value-pair len != 2.\n%s\n", kvPair)
}
}
r.Pairs[kvPair[0]] = kvPair[len(kvPair)-1]

View File

@@ -11,16 +11,18 @@ func init() {
rand.Seed(time.Now().UnixNano())
}
// CreateStreamSession creates a new STREAM Session.
// Returns the Id for the new Client.
func (c *Client) CreateStreamSession(id int32, dest string) (string, error) {
// CreateSession creates a new Session of type style, with an optional destination.
// an empty destination is interpreted as "TRANSIENT"
// Returns the destination for the new Client or an error.
func (c *Client) CreateSession(style, dest string) (string, error) {
if dest == "" {
dest = "TRANSIENT"
}
c.id = id
// c.id = id
r, err := c.sendCmd(
"SESSION CREATE STYLE=STREAM ID=%d DESTINATION=%s %s %s %s %s \n",
c.id,
"SESSION CREATE STYLE=%s ID=%s DESTINATION=%s %s %s %s %s \n",
style,
c.ID(),
dest,
c.from(),
c.to(),
@@ -43,3 +45,21 @@ func (c *Client) CreateStreamSession(id int32, dest string) (string, error) {
c.destination = r.Pairs["DESTINATION"]
return c.destination, nil
}
// CreateStreamSession creates a new STREAM Session.
// Returns the Id for the new Client.
func (c *Client) CreateStreamSession(dest string) (string, error) {
return c.CreateSession("STREAM", dest)
}
// CreateDatagramSession creates a new DATAGRAM Session.
// Returns the Id for the new Client.
func (c *Client) CreateDatagramSession(dest string) (string, error) {
return c.CreateSession("DATAGRAM", dest)
}
// CreateRawSession creates a new RAW Session.
// Returns the Id for the new Client.
func (c *Client) CreateRawSession(dest string) (string, error) {
return c.CreateSession("RAW", dest)
}

View File

@@ -5,8 +5,11 @@ import (
)
// StreamConnect asks SAM for a TCP-Like connection to dest, has to be called on a new Client
func (c *Client) StreamConnect(id int32, dest string) error {
r, err := c.sendCmd("STREAM CONNECT ID=%d DESTINATION=%s %s %s\n", id, dest, c.from(), c.to())
func (c *Client) StreamConnect(dest string) error {
if dest == "" {
return nil
}
r, err := c.sendCmd("STREAM CONNECT ID=%s DESTINATION=%s %s %s\n", c.ID(), dest, c.from(), c.to())
if err != nil {
return err
}
@@ -25,8 +28,8 @@ func (c *Client) StreamConnect(id int32, dest string) error {
}
// StreamAccept asks SAM to accept a TCP-Like connection
func (c *Client) StreamAccept(id int32) (*Reply, error) {
r, err := c.sendCmd("STREAM ACCEPT ID=%d SILENT=false\n", id)
func (c *Client) StreamAccept() (*Reply, error) {
r, err := c.sendCmd("STREAM ACCEPT ID=%s SILENT=false\n", c.ID())
if err != nil {
return nil, err
}