31 Commits

Author SHA1 Message Date
idk
38ca0d08e7 new version 2020-11-23 20:41:35 -05:00
idk
a9bf9faba1 new version 2020-11-23 20:40:34 -05:00
idk
3c6a72d179 move samsocks into this repository and treat it as an example application, since it's so simple 2020-11-22 12:46:16 -05:00
idk
e8b7525950 move samsocks into this repository and treat it as an example application, since it's so simple 2020-11-22 12:41:24 -05:00
idk
a0407fd3e3 move samsocks into this repository and treat it as an example application, since it's so simple 2020-11-22 12:39:20 -05:00
idk
3c5397e87f Fix session ID issue, we only need a new session when we really need a new session, i.e. give people a way to ask for it instead of trying to guess 2020-11-22 12:31:20 -05:00
idk
9baee36493 Try and return a new connection when we see the old one fail 2020-11-22 10:56:51 -05:00
idk
379de14264 Try and return a new connection when we see the old one fail 2020-11-22 10:56:35 -05:00
idk
dc12ba56d4 re-write some tests 2020-11-12 22:49:44 -05:00
idk
281084cb81 re-write some tests 2020-11-12 22:46:43 -05:00
idk
ff6b890bfd re-write some tests 2020-11-12 22:42:43 -05:00
idk
12d1bf38b2 re-write some tests 2020-11-12 22:41:05 -05:00
idk
7fa46ffc98 add resolve function compatible with SOCKS proxies 2020-09-13 16:51:06 -04:00
idk
a516752491 add resolve function compatible with SOCKS proxies 2020-09-13 01:32:22 -04:00
idk
4c81f5f7a0 fix mistaken identity issue with listeners 2020-09-03 21:51:25 -04:00
idk
a13b920f05 fix mistaken identity issue with listeners 2020-09-03 21:49:16 -04:00
idk
e5d5a0360b fix godoc 2020-09-03 16:47:32 -04:00
idk
460bd1b8f4 update debian changelog 2020-09-03 16:25:27 -04:00
idk
7d820eeaaa dual-keys by default, tidy up 2020-09-03 16:21:53 -04:00
idk
ac54a46ded Use dual keys by default 2020-09-03 16:19:12 -04:00
idk
22a3e9f3a7 Merge pull request #3 from bonedaddy/fix/mutex
Fix Pass Lock By Value Vet Warning
2020-09-02 12:04:38 +00:00
idk
b7ce5c1061 Merge pull request #2 from bonedaddy/feat/dest#create
Enable Basic Destination Creation
2020-09-02 12:03:43 +00:00
postables
7729762ce9 fix pass lock by value warning 2020-08-30 18:34:41 -07:00
postables
13bf63f35a test: make sure to check the returned error from destination creation 2020-08-30 17:59:30 -07:00
postables
91fc13b151 enable creating I2P destinations 2020-08-30 17:56:05 -07:00
idk
7cf9e8b61e mutex thing should be better 2020-08-25 16:53:59 -04:00
idk
c7d6848930 Protext non-context-aware dialer with a mutex 2020-08-25 10:31:44 -04:00
idk
eabd2d94f6 Protext non-context-aware dialer with a mutex 2020-08-25 10:27:40 -04:00
idk
76924e5961 add make link target 2020-07-29 13:00:36 -04:00
idk
027983674b add Makefile to help me automate releases 2020-07-29 12:57:16 -04:00
idk
7521a7862a Fix multi-lookup utility 2020-07-29 12:54:09 -04:00
20 changed files with 459 additions and 35 deletions

2
.gitignore vendored
View File

@@ -22,3 +22,5 @@ _testmain.go
*.exe
itp-golang-github-eyedeekay-gosam.txt
.pc
deb/
samsocks/samsocks

27
Makefile Normal file
View File

@@ -0,0 +1,27 @@
USER_GH=eyedeekay
VERSION=0.32.29
packagename=gosam
echo: fmt
@echo "type make version to do release $(VERSION)"
version:
gothub release -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION) -d "version $(VERSION)"
del:
gothub delete -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION)
tar:
tar --exclude .git \
--exclude .go \
--exclude bin \
--exclude examples \
-cJvf ../$(packagename)_$(VERSION).orig.tar.xz .
link:
rm -f ../goSam
ln -sf . ../goSam
fmt:
gofmt -w -s *.go */*.go

View File

@@ -3,10 +3,10 @@ goSam
A go library for using the [I2P](https://geti2p.net/en/) Simple Anonymous
Messaging ([SAM version 3.0](https://geti2p.net/en/docs/api/samv3)) bridge. It
has support for SAM version 3.1 signature types.
has support for all streaming features SAM version 3.2.
This is in an **early development stage**. I would love to hear about any
issues or ideas for improvement.
This is widely used and easy to use, but thusfar, mostly by me. It sees a lot of
testing and no breaking changes to the API are expected.
## Installation
```
@@ -15,7 +15,7 @@ go get github.com/eyedeekay/goSam
## Using it for HTTP Transport
I implemented `Client.Dial` like `net.Dial` so you can use go's library packages like http.
`Client.Dial` implements `net.Dial` so you can use go's library packages like http.
```go
package main
@@ -70,6 +70,52 @@ func checkErr(err error) {
}
```
## Using it as a SOCKS proxy
`client` also implements a resolver compatible with
[`getlantern/go-socks5`](https://github.com/getlantern/go-socks5),
making it very easy to implement a SOCKS5 server.
```go
package main
import (
"flag"
"github.com/eyedeekay/goSam"
"github.com/getlantern/go-socks5"
"log"
)
var (
samaddr = flag.String("sam", "127.0.0.1:7656", "SAM API address to use")
socksaddr = flag.String("socks", "127.0.0.1:7675", "SOCKS address to use")
)
func main() {
sam, err := goSam.NewClient(*samaddr)
if err != nil {
panic(err)
}
log.Println("Client Created")
// create a transport that uses SAM to dial TCP Connections
conf := &socks5.Config{
Dial: sam.DialContext,
Resolver: sam,
}
server, err := socks5.New(conf)
if err != nil {
panic(err)
}
// Create SOCKS5 proxy on localhost port 8000
if err := server.ListenAndServe("tcp", *socksaddr); err != nil {
panic(err)
}
}
```
### .deb package
A package for installing this on Debian is buildable, and a version for Ubuntu
@@ -85,7 +131,10 @@ to produce an unsigned deb for personal use only. For packagers,
will produce a viable source package for use with Launchpad PPA's and other
similar systems.
### TODO
* Implement `STREAM ACCEPT` and `STREAM FORWARD`
* Improve recovery on failed sockets
* Implement `STREAM FORWARD`
* Implement datagrams (Repliable and Anon)

View File

@@ -24,19 +24,19 @@ func (c *Client) Listen() (net.Listener, error) {
// with Accept
func (c *Client) ListenI2P(dest string) (net.Listener, error) {
var err error
var id int32
c.id = c.NewID()
c.destination, err = c.CreateStreamSession(id, dest)
c.destination, err = c.CreateStreamSession(c.id, dest)
d := c.destination
if err != nil {
return nil, err
}
fmt.Println("destination:", c.destination)
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)

View File

@@ -11,6 +11,7 @@ import (
"math/rand"
"net"
"strings"
"sync"
)
// A Client represents a single Connection to the SAM bridge
@@ -38,6 +39,7 @@ type Client struct {
dontPublishLease bool
encryptLease bool
leaseSetEncType string
reduceIdle bool
reduceIdleTime uint
@@ -52,6 +54,8 @@ type Client struct {
//NEVER, EVER modify lastaddr or id yourself. They are used internally only.
lastaddr string
id int32
ml sync.Mutex
oml sync.Mutex
}
var SAMsigTypes = []string{
@@ -89,13 +93,17 @@ func (c *Client) Destination() string {
// Base32 returns the base32 of the local tunnel
func (c *Client) Base32() string {
hash := sha256.New()
// hash := sha256.New()
b64, err := i2pB64enc.DecodeString(c.Base64())
if err != nil {
return ""
}
hash.Write([]byte(b64))
return strings.ToLower(strings.Replace(i2pB32enc.EncodeToString(hash.Sum(nil)), "=", "", -1))
//hash.Write([]byte(b64))
var s []byte
for _, e := range sha256.Sum256(b64) {
s = append(s, e)
}
return strings.ToLower(strings.Replace(i2pB32enc.EncodeToString(s), "=", "", -1))
}
func (c *Client) base64() []byte {
@@ -137,6 +145,9 @@ func NewClientFromOptions(opts ...func(*Client) error) (*Client, error) {
c.id = 0
c.lastaddr = "invalid"
c.destination = ""
c.leaseSetEncType = "4,0"
c.fromport = ""
c.toport = ""
for _, o := range opts {
if err := o(&c); err != nil {
return nil, err
@@ -175,7 +186,7 @@ func (c *Client) hello() error {
}
if r.Topic != "HELLO" {
return fmt.Errorf("Unknown Reply: %+v\n", r)
return fmt.Errorf("Client Hello Unknown Reply: %+v\n", r)
}
if r.Pairs["RESULT"] != "OK" {

View File

@@ -4,6 +4,20 @@ package goSam
import "testing"
import (
"fmt"
"time"
//"log"
"net/http"
"github.com/eyedeekay/sam3/helper"
"github.com/eyedeekay/sam3/i2pkeys"
)
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) {
@@ -16,6 +30,65 @@ func setup(t *testing.T) {
}
}
func TestCompositeClient(t *testing.T) {
listener, err := sam.I2PListener("testservice", "127.0.0.1:7656", "testkeys")
if err != nil {
t.Fatalf("Listener() Error: %q\n", err)
}
http.HandleFunc("/", HelloServer)
go http.Serve(listener, nil)
listener2, err := sam.I2PListener("testservice2", "127.0.0.1:7656", "testkeys2")
if err != nil {
t.Fatalf("Listener() Error: %q\n", err)
}
// http.HandleFunc("/", HelloServer)
go http.Serve(listener2, nil)
listener3, err := sam.I2PListener("testservice3", "127.0.0.1:7656", "testkeys3")
if err != nil {
t.Fatalf("Listener() Error: %q\n", err)
}
// http.HandleFunc("/", HelloServer)
go http.Serve(listener3, nil)
client, err = NewClientFromOptions(SetDebug(true))
if err != nil {
t.Fatalf("NewDefaultClient() Error: %q\n", err)
}
tr := &http.Transport{
Dial: client.Dial,
}
client := &http.Client{Transport: tr}
time.Sleep(time.Second * 30)
go func() {
resp, err := client.Get("http://" + listener.Addr().(i2pkeys.I2PAddr).Base32())
if err != nil {
t.Fatalf("Get Error: %q\n", err)
}
defer resp.Body.Close()
t.Log("Get returned ", resp)
}()
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)
}()
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)
}
func teardown(t *testing.T) {
if err := client.Close(); err != nil {
t.Fatalf("client.Close() Error: %q\n", err)
@@ -27,3 +100,12 @@ func TestClientHello(t *testing.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)
}
teardown(t)
}

42
debian/changelog vendored
View File

@@ -1,3 +1,45 @@
golang-github-eyedeekay-gosam (0.32.29) UNRELEASED; urgency=medium
* Maintenance updates
-- idk <hankhill19580@gmail.com> Mon, 23 Nov 2020 20:40:4 -0500
golang-github-eyedeekay-gosam (0.32.27) UNRELEASED; urgency=medium
* Maintenance updates
-- idk <hankhill19580@gmail.com> Thu, 12 Sept 2020 22:44:27 -0500
golang-github-eyedeekay-gosam (0.32.27) UNRELEASED; urgency=medium
* Add a Resolve function to fulfill SOCKS5 proxy requirements
-- idk <hankhill19580@gmail.com> Sun, 13 Sept 2020 04:48:27 -0500
golang-github-eyedeekay-gosam (0.32.26) UNRELEASED; urgency=medium
* Fix mistaken-identity issue with listeners
-- idk <hankhill19580@gmail.com> Thu, 03 Sept 2020 08:17:40 -0500
golang-github-eyedeekay-gosam (0.32.25) UNRELEASED; urgency=medium
* Support dual-keys by default in all future versions
-- idk <hankhill19580@gmail.com> Thu, 03 Sept 2020 04:25:04 -0500
golang-github-eyedeekay-gosam (0.32.24) UNRELEASED; urgency=medium
* Improve the mutex thingy
-- idk <hankhill19580@gmail.com> Tue, 25 Aug 2020 04:52:11 -0500
golang-github-eyedeekay-gosam (0.32.23) UNRELEASED; urgency=medium
* Protect Dial with a mutex to fix a lookup bug
-- idk <hankhill19580@gmail.com> Tue, 25 Aug 2020 10:29:26 -0500
golang-github-eyedeekay-gosam (0.3.2.1) bionic; urgency=medium
* Get rid of the debug directory, just move it into the source

3
debian/control vendored
View File

@@ -6,7 +6,8 @@ Uploaders: idk <hankhill19580@gmail.com>
Build-Depends: debhelper (>= 11),
dh-golang,
golang-any,
i2pd | i2p
i2pd | i2p,
git,
Standards-Version: 4.2.1
Homepage: https://github.com/eyedeekay/gosam
Vcs-Browser: https:/github.com/eyedeekay/gosam

18
dial.go
View File

@@ -9,20 +9,28 @@ import (
// 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()
errCh := make(chan error, 1)
connCh := make(chan net.Conn, 1)
go func() {
if conn, err := c.Dial(network, addr); err != nil {
errCh <- err
} else if ctx.Err() != nil {
conn.Close()
var err error
c, err = c.NewClient()
if err != nil {
conn.Close()
}
} else {
connCh <- conn
}
}()
select {
case err := <-errCh:
return nil, err
// var err error
c, err = c.NewClient()
return c.SamConn, err
case conn := <-connCh:
return conn, nil
case <-ctx.Done():
@@ -30,16 +38,20 @@ func (c *Client) DialContext(ctx context.Context, network, addr string) (net.Con
}
}
func (c Client) dialCheck(addr string) (int32, bool) {
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
}
// 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()
portIdx := strings.Index(addr, ":")
if portIdx >= 0 {
addr = addr[:portIdx]

7
go.mod
View File

@@ -1,6 +1,11 @@
module github.com/eyedeekay/goSam
require github.com/eyedeekay/sam3 v0.32.2
require (
github.com/eyedeekay/sam3 v0.32.32-0.20201122050855-f464873c9350
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
)
//replace github.com/eyedeekay/gosam v0.1.1-0.20190814195658-27e786578944 => github.com/eyedeekay/goSam ./

31
go.sum Normal file
View File

@@ -0,0 +1,31 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eyedeekay/ramp v0.0.0-20190429201811-305b382042ab h1:EfTRHxGSbiaEyxNzvKRBWVIDw3mD8xXGxj4gvwFzY7Q=
github.com/eyedeekay/ramp v0.0.0-20190429201811-305b382042ab/go.mod h1:h7mvUAMgZ/rtRDUOkvKTK+8LnDMeUhJSoa5EPdB51fc=
github.com/eyedeekay/sam3 v0.32.2 h1:xODDY5nBVg0oK7KaYk7ofkXFoHPsmI1umhSv1TZlS7s=
github.com/eyedeekay/sam3 v0.32.2/go.mod h1:Y3igFVzN4ybqkkpfUWULGhw7WRp8lieq0ORXbLBbcZM=
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/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=
github.com/getlantern/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
github.com/getlantern/go-socks5 v0.0.0-20171114193258-79d4dd3e2db5 h1:RBKofGGMt2k6eGBwX8mky9qunjL+KnAp9JdzXjiRkRw=
github.com/getlantern/go-socks5 v0.0.0-20171114193258-79d4dd3e2db5/go.mod h1:kGHRXch95rnGLHjER/GhhFiHvfnqNz7KqWD9kGfATHY=
github.com/getlantern/golog v0.0.0-20201105130739-9586b8bde3a9 h1:8MYJU90rB1bsavemKSAuDKBjtAKo5xq95bEPOnzV7CE=
github.com/getlantern/golog v0.0.0-20201105130739-9586b8bde3a9/go.mod h1:ZyIjgH/1wTCl+B+7yH1DqrWp6MPJqESmwmEQ89ZfhvA=
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 h1:micT5vkcr9tOVk1FiH8SWKID8ultN44Z+yzd2y/Vyb0=
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o=
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 h1:XYzSdCbkzOC0FDNrgJqGRo8PCMFOBFL9py72DRs7bmc=
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA=
github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd h1:mn98vs69Kqw56iKhR82mjk16Q1q5aDFFW0E89/QbXkQ=
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/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=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=

35
i2pkeys.go Normal file
View File

@@ -0,0 +1,35 @@
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) {
var (
sigtmp string
keys i2pkeys.I2PKeys
)
if len(sigType) > 0 {
sigtmp = sigType[0]
}
r, err := c.sendCmd(
"DEST GENERATE %s\n",
sigtmp,
)
if err != nil {
return keys, err
}
var pub, priv string
if priv = r.Pairs["PRIV"]; priv == "" {
return keys, 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 i2pkeys.NewKeys(i2pkeys.I2PAddr(pub), priv), nil
}

View File

@@ -1,7 +1,10 @@
package goSam
import (
"context"
"fmt"
"io"
"net"
"os"
)
@@ -14,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("Unknown Reply: %+v\n", r)
return "", fmt.Errorf("Naming Unknown Reply: %+v\n", r)
}
result := r.Pairs["RESULT"]
@@ -32,3 +35,39 @@ func (c *Client) Lookup(name string) (string, error) {
return r.Pairs["VALUE"], nil
}
func (c *Client) forward(client, conn net.Conn) {
go func() {
defer client.Close()
defer conn.Close()
io.Copy(client, conn)
}()
go func() {
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)
}
}()
}
return ctx, nil, nil
}

View File

@@ -22,12 +22,12 @@ func TestClientLookupInvalid(t *testing.T) {
if !ok {
t.Fatalf("client.Lookup() should return a ReplyError")
}
if repErr.Result != ResultInvalidKey {
t.Errorf("client.Lookup() should throw an ResultKeyNotFound error.\nGot:%+v\n", repErr)
if repErr.Result != ResultKeyNotFound {
t.Errorf("client.Lookup() should throw an ResultKeyNotFound error.\nGot:%+v%s%s\n", repErr, "!=", ResultKeyNotFound)
}
}
func ExampleClient_Lookup() {
func TestClientLookupValid(t *testing.T) {
client, err := NewDefaultClient()
if err != nil {
fmt.Printf("NewDefaultClient() should not throw an error.\n%s\n", err)
@@ -40,11 +40,17 @@ func ExampleClient_Lookup() {
return
}
if addr == `GKapJ8koUcBj~jmQzHsTYxDg2tpfWj0xjQTzd8BhfC9c3OS5fwPBNajgF-eOD6eCjFTqTlorlh7Hnd8kXj1qblUGXT-tDoR9~YV8dmXl51cJn9MVTRrEqRWSJVXbUUz9t5Po6Xa247Vr0sJn27R4KoKP8QVj1GuH6dB3b6wTPbOamC3dkO18vkQkfZWUdRMDXk0d8AdjB0E0864nOT~J9Fpnd2pQE5uoFT6P0DqtQR2jsFvf9ME61aqLvKPPWpkgdn4z6Zkm-NJOcDz2Nv8Si7hli94E9SghMYRsdjU-knObKvxiagn84FIwcOpepxuG~kFXdD5NfsH0v6Uri3usE3XWD7Pw6P8qVYF39jUIq4OiNMwPnNYzy2N4mDMQdsdHO3LUVh~DEppOy9AAmEoHDjjJxt2BFBbGxfdpZCpENkwvmZeYUyNCCzASqTOOlNzdpne8cuesn3NDXIpNnqEE6Oe5Qm5YOJykrX~Vx~cFFT3QzDGkIjjxlFBsjUJyYkFjBQAEAAcAAA==` {
t.Log("Success")
} else {
t.Errorf("Address of zzz.i2p != \nGKapJ8koUcBj~jmQzHsTYxDg2tpfWj0xjQTzd8BhfC9c3OS5fwPBNajgF-eOD6eCjFTqTlorlh7Hnd8kXj1qblUGXT-tDoR9~YV8dmXl51cJn9MVTRrEqRWSJVXbUUz9t5Po6Xa247Vr0sJn27R4KoKP8QVj1GuH6dB3b6wTPbOamC3dkO18vkQkfZWUdRMDXk0d8AdjB0E0864nOT~J9Fpnd2pQE5uoFT6P0DqtQR2jsFvf9ME61aqLvKPPWpkgdn4z6Zkm-NJOcDz2Nv8Si7hli94E9SghMYRsdjU-knObKvxiagn84FIwcOpepxuG~kFXdD5NfsH0v6Uri3usE3XWD7Pw6P8qVYF39jUIq4OiNMwPnNYzy2N4mDMQdsdHO3LUVh~DEppOy9AAmEoHDjjJxt2BFBbGxfdpZCpENkwvmZeYUyNCCzASqTOOlNzdpne8cuesn3NDXIpNnqEE6Oe5Qm5YOJykrX~Vx~cFFT3QzDGkIjjxlFBsjUJyYkFjBQAEAAcAAA==\n, check to see if it changed, %s", addr)
}
fmt.Println("Address of zzz.i2p:")
// Addresses change all the time
fmt.Println(addr)
// Output:
//Address of zzz.i2p:
//GKapJ8koUcBj~jmQzHsTYxDg2tpfWj0xjQTzd8BhfC9c3OS5fwPBNajgF-eOD6eCjFTqTlorlh7Hnd8kXj1qblUGXT-tDoR9~YV8dmXl51cJn9MVTRrEqRWSJVXbUUz9t5Po6Xa247Vr0sJn27R4KoKP8QVj1GuH6dB3b6wTPbOamC3dkO18vkQkfZWUdRMDXk0d8AdjB0E0864nOT~J9Fpnd2pQE5uoFT6P0DqtQR2jsFvf9ME61aqLvKPPWpkgdn4z6Zkm-NJOcDz2Nv8Si7hli94E9SghMYRsdjU-knObKvxiagn84FIwcOpepxuG~kFXdD5NfsH0v6Uri3usE3uSzpWS0EHmrlfoLr5uGGd9ZHwwCIcgfOATaPRMUEQxiK9q48PS0V3EXXO4-YLT0vIfk4xO~XqZpn8~PW1kFe2mQMHd7oO89yCk-3yizRG3UyFtI7-mO~eCI6-m1spYoigStgoupnC3G85gJkqEjMm49gUjbhfWKWI-6NwTj0ZnAAAA
//
}

View File

@@ -275,6 +275,15 @@ func SetEncrypt(b bool) func(*Client) error {
}
}
//SetLeaseSetEncType tells the router to use an encrypted leaseset of a specific type.
//defaults to 4,0
func SetLeaseSetEncType(b string) func(*Client) error {
return func(c *Client) error {
c.leaseSetEncType = b
return nil
}
}
//SetReduceIdle sets the created tunnels to be reduced during extended idle time to avoid excessive resource usage
func SetReduceIdle(b bool) func(*Client) error {
return func(c *Client) error {
@@ -360,6 +369,12 @@ func SetSignatureType(s string) func(*Client) error {
//return the from port as a string.
func (c *Client) from() string {
if c.fromport == "FROM_PORT=0" {
return ""
}
if c.fromport == "0" {
return ""
}
if c.fromport == "" {
return ""
}
@@ -368,6 +383,12 @@ func (c *Client) from() string {
//return the to port as a string.
func (c *Client) to() string {
if c.fromport == "TO_PORT=0" {
return ""
}
if c.fromport == "0" {
return ""
}
if c.toport == "" {
return ""
}
@@ -426,6 +447,13 @@ func (c *Client) encryptlease() string {
return " i2cp.encryptLeaseSet=false "
}
func (c *Client) leasesetenctype() string {
if c.encryptLease {
return fmt.Sprintf(" i2cp.leaseSetEncType=%s ", c.leaseSetEncType)
}
return " i2cp.leaseSetEncType=4,0 "
}
func (c *Client) dontpublishlease() string {
if c.dontPublishLease {
return " i2cp.dontPublishLeaseSet=true "
@@ -478,6 +506,7 @@ func (c *Client) allOptions() string {
c.outbackups() +
c.dontpublishlease() +
c.encryptlease() +
c.leasesetenctype() +
c.reduceonidle() +
c.reduceidletime() +
c.reduceidlecount() +
@@ -498,6 +527,7 @@ func (c *Client) Print() string {
c.outbackups() +
c.dontpublishlease() +
c.encryptlease() +
c.leasesetenctype() +
c.reduceonidle() +
c.reduceidletime() +
c.reduceidlecount() +

View File

@@ -2,6 +2,8 @@
package goSam
/*
import (
"fmt"
"math"
@@ -186,3 +188,4 @@ func TestOptionPortInt(t *testing.T) {
fmt.Printf("\t address64- %s \t", client.Base64())
fmt.Printf("\t address- %s \t", client.Base32())
}
*/

View File

@@ -32,6 +32,8 @@ func (r ReplyError) Error() string {
type Reply struct {
Topic string
Type string
From string
To string
Pairs map[string]string
}
@@ -50,14 +52,24 @@ func parseReply(line string) (*Reply, error) {
}
for _, v := range parts[2:] {
kvPair := strings.SplitN(v, "=", 2)
if kvPair != nil {
if len(kvPair) != 2 {
return nil, fmt.Errorf("Malformed key-value-pair.\n%s\n", kvPair)
if strings.Contains(v, "FROM_PORT") {
if v != "FROM_PORT=0" {
r.From = v
}
} else if strings.Contains(v, "TO_PORT") {
if v != "TO_PORT=0" {
r.To = v
}
} 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)
}
}
r.Pairs[kvPair[0]] = kvPair[len(kvPair)-1]
}
r.Pairs[kvPair[0]] = kvPair[len(kvPair)-1]
}
return r, nil

37
samsocks/main.go Normal file
View File

@@ -0,0 +1,37 @@
package main
import (
"flag"
"github.com/eyedeekay/goSam"
"github.com/getlantern/go-socks5"
"log"
)
var (
samaddr = flag.String("sam", "127.0.0.1:7656", "SAM API address to use")
socksaddr = flag.String("socks", "127.0.0.1:7675", "SOCKS address to use")
)
func main() {
sam, err := goSam.NewClient(*samaddr)
if err != nil {
panic(err)
}
log.Println("Client Created")
// create a transport that uses SAM to dial TCP Connections
conf := &socks5.Config{
Dial: sam.DialContext,
Resolver: sam,
}
server, err := socks5.New(conf)
if err != nil {
panic(err)
}
// Create SOCKS5 proxy on localhost port 8000
if err := server.ListenAndServe("tcp", *socksaddr); err != nil {
panic(err)
}
}

View File

@@ -19,11 +19,11 @@ func (c *Client) CreateStreamSession(id int32, dest string) (string, error) {
}
c.id = id
r, err := c.sendCmd(
"SESSION CREATE STYLE=STREAM ID=%d %s %s DESTINATION=%s %s %s\n",
"SESSION CREATE STYLE=STREAM ID=%d DESTINATION=%s %s %s %s %s \n",
c.id,
dest,
c.from(),
c.to(),
dest,
c.sigtype(),
c.allOptions(),
)
@@ -33,7 +33,7 @@ func (c *Client) CreateStreamSession(id int32, dest string) (string, error) {
// TODO: move check into sendCmd()
if r.Topic != "SESSION" || r.Type != "STATUS" {
return "", fmt.Errorf("Unknown Reply: %+v\n", r)
return "", fmt.Errorf("Session Unknown Reply: %+v\n", r)
}
result := r.Pairs["RESULT"]

View File

@@ -6,14 +6,14 @@ 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 %s %s DESTINATION=%s\n", id, c.from(), c.to(), dest)
r, err := c.sendCmd("STREAM CONNECT ID=%d DESTINATION=%s %s %s\n", id, dest, c.from(), c.to())
if err != nil {
return err
}
// TODO: move check into sendCmd()
if r.Topic != "STREAM" || r.Type != "STATUS" {
return fmt.Errorf("Unknown Reply: %+v\n", r)
return fmt.Errorf("Stream Connect Unknown Reply: %+v\n", r)
}
result := r.Pairs["RESULT"]
@@ -33,7 +33,7 @@ func (c *Client) StreamAccept(id int32) (*Reply, error) {
// TODO: move check into sendCmd()
if r.Topic != "STREAM" || r.Type != "STATUS" {
return nil, fmt.Errorf("Unknown Reply: %+v\n", r)
return nil, fmt.Errorf("Stream Accept Unknown Reply: %+v\n", r)
}
result := r.Pairs["RESULT"]