Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b9e4186119 | ||
![]() |
3f57ff4eaf | ||
![]() |
4c6e2f5d88 |
8
Makefile
8
Makefile
@@ -1,16 +1,16 @@
|
|||||||
|
|
||||||
USER_GH=eyedeekay
|
USER_GH=eyedeekay
|
||||||
VERSION=0.33.4
|
VERSION=0.33.001
|
||||||
packagename=sam3
|
packagename=sam3
|
||||||
|
|
||||||
echo:
|
echo:
|
||||||
@echo "type make version to do release $(VERSION)"
|
@echo "type make version to do release $(VERSION)"
|
||||||
|
|
||||||
version:
|
version:
|
||||||
github-release release -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION) -d "version $(VERSION)"
|
gothub release -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION) -d "version $(VERSION)"
|
||||||
|
|
||||||
del:
|
del:
|
||||||
github-release delete -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION)
|
gothub delete -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION)
|
||||||
|
|
||||||
tar:
|
tar:
|
||||||
tar --exclude .git \
|
tar --exclude .git \
|
||||||
@@ -26,4 +26,4 @@ fmt:
|
|||||||
find . -name '*.go' -exec gofmt -w -s {} \;
|
find . -name '*.go' -exec gofmt -w -s {} \;
|
||||||
|
|
||||||
upload-linux:
|
upload-linux:
|
||||||
github-release upload -R -u $(USER_GH) -r "$(packagename)" -t $(VERSION) -l `sha256sum ` -n "$(packagename)" -f "$(packagename)"
|
gothub upload -R -u $(USER_GH) -r "$(packagename)" -t $(VERSION) -l `sha256sum ` -n "$(packagename)" -f "$(packagename)"
|
||||||
|
27
README.md
27
README.md
@@ -1,17 +1,5 @@
|
|||||||
# README #
|
# README #
|
||||||
|
|
||||||
## !!IMPORTANT!!
|
|
||||||
|
|
||||||
In the next version, I'll be moving the `i2pkeys` directory to it's own repository
|
|
||||||
so I can avoid import cycle headaches. Please migrate to the new `i2pkeys` repository
|
|
||||||
before upgrading your sam3 dependencies. You can probably do this by running:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
find . -name '*.go' -exec sed -i 's|github.com/eyedeekay/sam3/i2pkeys|github.com/eyedeekay/i2pkeys|g' {} \;
|
|
||||||
```
|
|
||||||
|
|
||||||
# README #
|
|
||||||
|
|
||||||
go library for the I2P [SAMv3.0](https://geti2p.net/en/docs/api/samv3) bridge, used to build anonymous/pseudonymous end-to-end encrypted sockets.
|
go library for the I2P [SAMv3.0](https://geti2p.net/en/docs/api/samv3) bridge, used to build anonymous/pseudonymous end-to-end encrypted sockets.
|
||||||
|
|
||||||
This library is much better than ccondom (that use BOB), much more stable and much easier to maintain.
|
This library is much better than ccondom (that use BOB), much more stable and much easier to maintain.
|
||||||
@@ -35,7 +23,7 @@ This library is much better than ccondom (that use BOB), much more stable and mu
|
|||||||
|
|
||||||
**Does not work:**
|
**Does not work:**
|
||||||
|
|
||||||
* Stream Forwarding
|
* Everything works! :D
|
||||||
* Probably needs some real-world testing
|
* Probably needs some real-world testing
|
||||||
|
|
||||||
## Documentation ##
|
## Documentation ##
|
||||||
@@ -50,17 +38,16 @@ This library is much better than ccondom (that use BOB), much more stable and mu
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/eyedeekay/sam3"
|
"github.com/majestrate/i2p-tools/sam3"
|
||||||
"github.com/eyedeekay/sam3/i2pkeys"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const yoursam = "127.0.0.1:7656" // sam bridge
|
const yoursam = "127.0.0.1:7656" // sam bridge
|
||||||
|
|
||||||
func client(server i2pkeys.I2PAddr) {
|
func client(server I2PAddr) {
|
||||||
sam, _ := sam3.NewSAM(yoursam)
|
sam, _ := NewSAM(yoursam)
|
||||||
keys, _ := sam.NewKeys()
|
keys, _ := sam.NewKeys()
|
||||||
stream, _ := sam.NewStreamSession("clientTun", keys, sam3.Options_Small)
|
stream, _ := sam.NewStreamSession("clientTun", keys, Options_Small)
|
||||||
fmt.Println("Client: Connecting to " + server.Base32())
|
fmt.Println("Client: Connecting to " + server.Base32())
|
||||||
conn, _ := stream.DialI2P(server)
|
conn, _ := stream.DialI2P(server)
|
||||||
conn.Write([]byte("Hello world!"))
|
conn.Write([]byte("Hello world!"))
|
||||||
@@ -70,9 +57,11 @@ func client(server i2pkeys.I2PAddr) {
|
|||||||
func main() {
|
func main() {
|
||||||
sam, _ := NewSAM(yoursam)
|
sam, _ := NewSAM(yoursam)
|
||||||
keys, _ := sam.NewKeys()
|
keys, _ := sam.NewKeys()
|
||||||
stream, _ := sam.NewStreamSession("serverTun", keys, sam3.Options_Medium)
|
stream, _ := sam.NewStreamSession("serverTun", keys, Options_Medium)
|
||||||
listener, _ := stream.Listen()
|
listener, _ := stream.Listen()
|
||||||
go client(keys.Addr())
|
go client(keys.Addr())
|
||||||
|
stream, _ := sam.NewStreamSession("serverTun", keys, Options_Medium)
|
||||||
|
listener, _ := stream.Listen()
|
||||||
conn, _ := listener.Accept()
|
conn, _ := listener.Accept()
|
||||||
buf := make([]byte, 4096)
|
buf := make([]byte, 4096)
|
||||||
n, _ := conn.Read(buf)
|
n, _ := conn.Read(buf)
|
||||||
|
@@ -1,15 +1,14 @@
|
|||||||
package sam3
|
package sam3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/eyedeekay/sam3/i2pkeys"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/eyedeekay/i2pkeys"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
import (
|
import (
|
||||||
. "github.com/eyedeekay/i2pkeys"
|
. "github.com/eyedeekay/sam3/i2pkeys"
|
||||||
)
|
)
|
||||||
*/
|
*/
|
||||||
// Implements net.Conn
|
// Implements net.Conn
|
||||||
|
@@ -7,7 +7,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/eyedeekay/i2pkeys"
|
"github.com/eyedeekay/sam3/i2pkeys"
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2PConfig is a struct which manages I2P configuration options
|
// I2PConfig is a struct which manages I2P configuration options
|
||||||
@@ -347,7 +347,7 @@ func (cfg *Config) DatagramSession() (session *DatagramSession, err error) {
|
|||||||
// determine udp port
|
// determine udp port
|
||||||
var portstr string
|
var portstr string
|
||||||
_, portstr, err = net.SplitHostPort(cfg.Addr)
|
_, portstr, err = net.SplitHostPort(cfg.Addr)
|
||||||
if IgnorePortError(err) == nil {
|
if err == nil {
|
||||||
var port int
|
var port int
|
||||||
port, err = strconv.Atoi(portstr)
|
port, err = strconv.Atoi(portstr)
|
||||||
if err == nil && port > 0 {
|
if err == nil && port > 0 {
|
||||||
|
18
datagram.go
18
datagram.go
@@ -7,7 +7,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/eyedeekay/i2pkeys"
|
"github.com/eyedeekay/sam3/i2pkeys"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The DatagramSession implements net.PacketConn. It works almost like ordinary
|
// The DatagramSession implements net.PacketConn. It works almost like ordinary
|
||||||
@@ -33,7 +33,7 @@ func (s *SAM) NewDatagramSession(id string, keys i2pkeys.I2PKeys, options []stri
|
|||||||
if udpPort == 0 {
|
if udpPort == 0 {
|
||||||
udpPort = 7655
|
udpPort = 7655
|
||||||
}
|
}
|
||||||
lhost, _, err := SplitHostPort(s.conn.LocalAddr().String())
|
lhost, _, err := net.SplitHostPort(s.conn.LocalAddr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Close()
|
s.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -46,7 +46,7 @@ func (s *SAM) NewDatagramSession(id string, keys i2pkeys.I2PKeys, options []stri
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rhost, _, err := SplitHostPort(s.conn.RemoteAddr().String())
|
rhost, _, err := net.SplitHostPort(s.conn.RemoteAddr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Close()
|
s.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -56,10 +56,6 @@ func (s *SAM) NewDatagramSession(id string, keys i2pkeys.I2PKeys, options []stri
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
|
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
|
||||||
if err != nil {
|
|
||||||
s.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
conn, err := s.newGenericSession("DATAGRAM", id, keys, options, []string{"PORT=" + lport})
|
conn, err := s.newGenericSession("DATAGRAM", id, keys, options, []string{"PORT=" + lport})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -88,13 +84,7 @@ func (s *DatagramSession) DialRemote(net, addr string) (net.PacketConn, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *DatagramSession) DialI2PRemote(net string, addr net.Addr) (*DatagramSession, error) {
|
func (s *DatagramSession) DialI2PRemote(net string, addr net.Addr) (*DatagramSession, error) {
|
||||||
switch addr.(type) {
|
s.remoteAddr = addr.(*i2pkeys.I2PAddr)
|
||||||
case *i2pkeys.I2PAddr:
|
|
||||||
s.remoteAddr = addr.(*i2pkeys.I2PAddr)
|
|
||||||
case i2pkeys.I2PAddr:
|
|
||||||
i2paddr := addr.(i2pkeys.I2PAddr)
|
|
||||||
s.remoteAddr = &i2paddr
|
|
||||||
}
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
// +build nettest
|
||||||
|
|
||||||
package sam3
|
package sam3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
24
emit.go
24
emit.go
@@ -3,8 +3,6 @@ package sam3
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SAMEmit struct {
|
type SAMEmit struct {
|
||||||
@@ -98,25 +96,3 @@ func NewEmit(opts ...func(*SAMEmit) error) (*SAMEmit, error) {
|
|||||||
}
|
}
|
||||||
return &emit, nil
|
return &emit, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func IgnorePortError(err error) error {
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if strings.Contains(err.Error(), "missing port in address") {
|
|
||||||
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.Println("host: " + hostport)
|
|
||||||
host = hostport
|
|
||||||
port = "0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return host, port, nil
|
|
||||||
}
|
|
||||||
|
1
go.mod
1
go.mod
@@ -4,7 +4,6 @@ go 1.12
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/eyedeekay/goSam v0.32.31-0.20210122211817-f97683379f23
|
github.com/eyedeekay/goSam v0.32.31-0.20210122211817-f97683379f23
|
||||||
github.com/eyedeekay/i2pkeys v0.0.0-20220310052025-204d4ae6dcae // indirect
|
|
||||||
github.com/google/renameio v1.0.0 // indirect
|
github.com/google/renameio v1.0.0 // indirect
|
||||||
github.com/riobard/go-x25519 v0.0.0-20190716001027-10cc4d8d0b33
|
github.com/riobard/go-x25519 v0.0.0-20190716001027-10cc4d8d0b33
|
||||||
github.com/rogpeppe/go-internal v1.6.2 // indirect
|
github.com/rogpeppe/go-internal v1.6.2 // indirect
|
||||||
|
2
go.sum
2
go.sum
@@ -12,8 +12,6 @@ github.com/eyedeekay/goSam v0.32.31-0.20210122211024-dddd8ea916d6 h1:seMFdfTWvmA
|
|||||||
github.com/eyedeekay/goSam v0.32.31-0.20210122211024-dddd8ea916d6/go.mod h1:UgJnih/LpotwKriwVPOEa6yPDM2NDdVrKfLtS5DOLPE=
|
github.com/eyedeekay/goSam v0.32.31-0.20210122211024-dddd8ea916d6/go.mod h1:UgJnih/LpotwKriwVPOEa6yPDM2NDdVrKfLtS5DOLPE=
|
||||||
github.com/eyedeekay/goSam v0.32.31-0.20210122211817-f97683379f23 h1:AHm/EzBilSQH+RFgEuslnlCpVQd88MQWx7KHW/VIQlc=
|
github.com/eyedeekay/goSam v0.32.31-0.20210122211817-f97683379f23 h1:AHm/EzBilSQH+RFgEuslnlCpVQd88MQWx7KHW/VIQlc=
|
||||||
github.com/eyedeekay/goSam v0.32.31-0.20210122211817-f97683379f23/go.mod h1:UgJnih/LpotwKriwVPOEa6yPDM2NDdVrKfLtS5DOLPE=
|
github.com/eyedeekay/goSam v0.32.31-0.20210122211817-f97683379f23/go.mod h1:UgJnih/LpotwKriwVPOEa6yPDM2NDdVrKfLtS5DOLPE=
|
||||||
github.com/eyedeekay/i2pkeys v0.0.0-20220310052025-204d4ae6dcae h1:SwegHeaf4pkDMB24UltIvJlj2+nd06QUZAbs8BDyfjM=
|
|
||||||
github.com/eyedeekay/i2pkeys v0.0.0-20220310052025-204d4ae6dcae/go.mod h1:W9KCm9lqZ+Ozwl3dwcgnpPXAML97+I8Jiht7o5A8YBM=
|
|
||||||
github.com/eyedeekay/sam3 v0.32.32/go.mod h1:qRA9KIIVxbrHlkj+ZB+OoxFGFgdKeGp1vSgPw26eOVU=
|
github.com/eyedeekay/sam3 v0.32.32/go.mod h1:qRA9KIIVxbrHlkj+ZB+OoxFGFgdKeGp1vSgPw26eOVU=
|
||||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
|
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
|
||||||
github.com/getlantern/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
|
github.com/getlantern/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
|
||||||
|
167
helper/helper.go
167
helper/helper.go
@@ -1,17 +1,18 @@
|
|||||||
package sam
|
package sam
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/eyedeekay/i2pkeys"
|
|
||||||
"github.com/eyedeekay/sam3"
|
"github.com/eyedeekay/sam3"
|
||||||
|
"github.com/eyedeekay/sam3/i2pkeys"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NetListener(name, samaddr, keyspath string) (net.Listener, error) {
|
func NetListener(name, samaddr, keyspath string) (net.Listener, error) {
|
||||||
return I2PListener(name, sam3.SAMDefaultAddr(samaddr), keyspath)
|
return I2PListener(name, samaddr, keyspath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2PListener is a convenience function which takes a SAM tunnel name, a SAM address and a filename.
|
// I2PListener is a convenience function which takes a SAM tunnel name, a SAM address and a filename.
|
||||||
@@ -19,16 +20,15 @@ func NetListener(name, samaddr, keyspath string) (net.Listener, error) {
|
|||||||
// exist, keys will be generated and stored in that file.
|
// exist, keys will be generated and stored in that file.
|
||||||
func I2PListener(name, samaddr, keyspath string) (*sam3.StreamListener, error) {
|
func I2PListener(name, samaddr, keyspath string) (*sam3.StreamListener, error) {
|
||||||
log.Printf("Starting and registering I2P service, please wait a couple of minutes...")
|
log.Printf("Starting and registering I2P service, please wait a couple of minutes...")
|
||||||
listener, err := I2PStreamSession(name, sam3.SAMDefaultAddr(samaddr), keyspath)
|
listener, err := I2PStreamSession(name, samaddr, keyspath)
|
||||||
|
|
||||||
if keyspath != "" {
|
if keyspath != "" {
|
||||||
err = ioutil.WriteFile(keyspath+".i2p.public.txt", []byte(listener.Keys().Addr().Base32()), 0644)
|
err = ioutil.WriteFile(keyspath+".i2p.public.txt", []byte(listener.Keys().Addr().Base32()), 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error storing I2P base32 address in adjacent text file, %s", err)
|
return nil, fmt.Errorf("error storing I2P base32 address in adjacent text file, %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Printf("Listening on: %s", listener.Addr().Base32())
|
return listener.Listen() //, err
|
||||||
return listener.Listen()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2PStreamSession is a convenience function which returns a sam3.StreamSession instead
|
// I2PStreamSession is a convenience function which returns a sam3.StreamSession instead
|
||||||
@@ -36,13 +36,40 @@ func I2PListener(name, samaddr, keyspath string) (*sam3.StreamListener, error) {
|
|||||||
// of the user.
|
// of the user.
|
||||||
func I2PStreamSession(name, samaddr, keyspath string) (*sam3.StreamSession, error) {
|
func I2PStreamSession(name, samaddr, keyspath string) (*sam3.StreamSession, error) {
|
||||||
log.Printf("Starting and registering I2P session...")
|
log.Printf("Starting and registering I2P session...")
|
||||||
sam, err := sam3.NewSAM(sam3.SAMDefaultAddr(samaddr))
|
sam, err := sam3.NewSAM(samaddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error connecting to SAM to %s: %s", sam3.SAMDefaultAddr(samaddr), err)
|
return nil, fmt.Errorf("error connecting to SAM to %s: %s", samaddr, err)
|
||||||
}
|
}
|
||||||
keys, err := GenerateOrLoadKeys(keyspath, sam)
|
var keys *i2pkeys.I2PKeys
|
||||||
if err != nil {
|
if keyspath != "" {
|
||||||
return nil, err
|
if _, err := os.Stat(keyspath + ".i2p.private"); os.IsNotExist(err) {
|
||||||
|
f, err := os.Create(keyspath + ".i2p.private")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to open I2P keyfile for writing: %s", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
tkeys, err := sam.NewKeys()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to generate I2P Keys, %s", err)
|
||||||
|
}
|
||||||
|
keys = &tkeys
|
||||||
|
err = i2pkeys.StoreKeysIncompat(*keys, f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to save newly generated I2P Keys, %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tkeys, err := i2pkeys.LoadKeys(keyspath + ".i2p.private")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to load I2P Keys: %e", err)
|
||||||
|
}
|
||||||
|
keys = &tkeys
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tkeys, err := sam.NewKeys()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to generate I2P Keys, %s", err)
|
||||||
|
}
|
||||||
|
keys = &tkeys
|
||||||
}
|
}
|
||||||
stream, err := sam.NewStreamSession(name, *keys, sam3.Options_Medium)
|
stream, err := sam.NewStreamSession(name, *keys, sam3.Options_Medium)
|
||||||
return stream, err
|
return stream, err
|
||||||
@@ -52,13 +79,40 @@ func I2PStreamSession(name, samaddr, keyspath string) (*sam3.StreamSession, erro
|
|||||||
// It also takes care of setting a persisitent key on behalf of the user.
|
// It also takes care of setting a persisitent key on behalf of the user.
|
||||||
func I2PDatagramSession(name, samaddr, keyspath string) (*sam3.DatagramSession, error) {
|
func I2PDatagramSession(name, samaddr, keyspath string) (*sam3.DatagramSession, error) {
|
||||||
log.Printf("Starting and registering I2P session...")
|
log.Printf("Starting and registering I2P session...")
|
||||||
sam, err := sam3.NewSAM(sam3.SAMDefaultAddr(samaddr))
|
sam, err := sam3.NewSAM(samaddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error connecting to SAM to %s: %s", sam3.SAMDefaultAddr(samaddr), err)
|
return nil, fmt.Errorf("error connecting to SAM to %s: %s", samaddr, err)
|
||||||
}
|
}
|
||||||
keys, err := GenerateOrLoadKeys(keyspath, sam)
|
var keys *i2pkeys.I2PKeys
|
||||||
if err != nil {
|
if keyspath != "" {
|
||||||
return nil, err
|
if _, err := os.Stat(keyspath + ".i2p.private"); os.IsNotExist(err) {
|
||||||
|
f, err := os.Create(keyspath + ".i2p.private")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to open I2P keyfile for writing: %s", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
tkeys, err := sam.NewKeys()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to generate I2P Keys, %s", err)
|
||||||
|
}
|
||||||
|
keys = &tkeys
|
||||||
|
err = i2pkeys.StoreKeysIncompat(*keys, f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to save newly generated I2P Keys, %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tkeys, err := i2pkeys.LoadKeys(keyspath + ".i2p.private")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to load I2P Keys: %e", err)
|
||||||
|
}
|
||||||
|
keys = &tkeys
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tkeys, err := sam.NewKeys()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to generate I2P Keys, %s", err)
|
||||||
|
}
|
||||||
|
keys = &tkeys
|
||||||
}
|
}
|
||||||
gram, err := sam.NewDatagramSession(name, *keys, sam3.Options_Medium, 0)
|
gram, err := sam.NewDatagramSession(name, *keys, sam3.Options_Medium, 0)
|
||||||
return gram, err
|
return gram, err
|
||||||
@@ -68,58 +122,41 @@ func I2PDatagramSession(name, samaddr, keyspath string) (*sam3.DatagramSession,
|
|||||||
// It also takes care of setting a persisitent key on behalf of the user.
|
// It also takes care of setting a persisitent key on behalf of the user.
|
||||||
func I2PPrimarySession(name, samaddr, keyspath string) (*sam3.PrimarySession, error) {
|
func I2PPrimarySession(name, samaddr, keyspath string) (*sam3.PrimarySession, error) {
|
||||||
log.Printf("Starting and registering I2P session...")
|
log.Printf("Starting and registering I2P session...")
|
||||||
sam, err := sam3.NewSAM(sam3.SAMDefaultAddr(samaddr))
|
sam, err := sam3.NewSAM(samaddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error connecting to SAM to %s: %s", sam3.SAMDefaultAddr(samaddr), err)
|
return nil, fmt.Errorf("error connecting to SAM to %s: %s", samaddr, err)
|
||||||
}
|
}
|
||||||
keys, err := GenerateOrLoadKeys(keyspath, sam)
|
var keys *i2pkeys.I2PKeys
|
||||||
if err != nil {
|
if keyspath != "" {
|
||||||
return nil, err
|
if _, err := os.Stat(keyspath + ".i2p.private"); os.IsNotExist(err) {
|
||||||
|
f, err := os.Create(keyspath + ".i2p.private")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to open I2P keyfile for writing: %s", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
tkeys, err := sam.NewKeys()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to generate I2P Keys, %s", err)
|
||||||
|
}
|
||||||
|
keys = &tkeys
|
||||||
|
err = i2pkeys.StoreKeysIncompat(*keys, f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to save newly generated I2P Keys, %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tkeys, err := i2pkeys.LoadKeys(keyspath + ".i2p.private")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to load I2P Keys: %e", err)
|
||||||
|
}
|
||||||
|
keys = &tkeys
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tkeys, err := sam.NewKeys()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to generate I2P Keys, %s", err)
|
||||||
|
}
|
||||||
|
keys = &tkeys
|
||||||
}
|
}
|
||||||
gram, err := sam.NewPrimarySession(name, *keys, sam3.Options_Medium)
|
gram, err := sam.NewPrimarySession(name, *keys, sam3.Options_Medium)
|
||||||
return gram, err
|
return gram, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateOrLoadKeys is a convenience function which takes a filename and a SAM session.
|
|
||||||
// if the SAM session is nil, a new one will be created with the defaults.
|
|
||||||
// The keyspath must be the path to a place to store I2P keys. The keyspath will be suffixed with
|
|
||||||
// .i2p.private for the private keys, and public.txt for the b32 addresses.
|
|
||||||
// If the keyspath.i2p.private file does not exist, keys will be generated and stored in that file.
|
|
||||||
// if the keyspath.i2p.private does exist, keys will be loaded from that location and returned
|
|
||||||
func GenerateOrLoadKeys(keyspath string, sam *sam3.SAM) (keys *i2pkeys.I2PKeys, err error) {
|
|
||||||
if sam == nil {
|
|
||||||
sam, err = sam3.NewSAM(sam3.SAMDefaultAddr("127.0.0.1:7656"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err := os.Stat(keyspath + ".i2p.private"); os.IsNotExist(err) {
|
|
||||||
f, err := os.Create(keyspath + ".i2p.private")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("unable to open I2P keyfile for writing: %s", err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
tkeys, err := sam.NewKeys()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("unable to generate I2P Keys, %s", err)
|
|
||||||
}
|
|
||||||
keys = &tkeys
|
|
||||||
err = i2pkeys.StoreKeysIncompat(*keys, f)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("unable to save newly generated I2P Keys, %s", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tkeys, err := i2pkeys.LoadKeys(keyspath + ".i2p.private")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("unable to load I2P Keys: %e", err)
|
|
||||||
}
|
|
||||||
keys = &tkeys
|
|
||||||
}
|
|
||||||
return keys, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateKeys is a shorter version of GenerateOrLoadKeys which generates keys and stores them in a file.
|
|
||||||
// it always uses a new default SAM session.
|
|
||||||
func GenerateKeys(keyspath string) (keys *i2pkeys.I2PKeys, err error) {
|
|
||||||
return GenerateOrLoadKeys(keyspath, nil)
|
|
||||||
}
|
|
||||||
|
334
i2pkeys/I2PAddr.go
Normal file
334
i2pkeys/I2PAddr.go
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
package i2pkeys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base32"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/eyedeekay/goSam"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
i2pB64enc *base64.Encoding = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~")
|
||||||
|
i2pB32enc *base32.Encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
|
||||||
|
)
|
||||||
|
|
||||||
|
var FakePort = false
|
||||||
|
|
||||||
|
// The public and private keys associated with an I2P destination. I2P hides the
|
||||||
|
// details of exactly what this is, so treat them as blobs, but generally: One
|
||||||
|
// pair of DSA keys, one pair of ElGamal keys, and sometimes (almost never) also
|
||||||
|
// a certificate. String() returns you the full content of I2PKeys and Addr()
|
||||||
|
// returns the public keys.
|
||||||
|
type I2PKeys struct {
|
||||||
|
Address I2PAddr // only the public key
|
||||||
|
Both string // both public and private keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates I2PKeys from an I2PAddr and a public/private keypair string (as
|
||||||
|
// generated by String().)
|
||||||
|
func NewKeys(addr I2PAddr, both string) I2PKeys {
|
||||||
|
return I2PKeys{addr, both}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileExists checks if a file exists and is not a directory before we
|
||||||
|
// try using it to prevent further errors.
|
||||||
|
func fileExists(filename string) (bool, error) {
|
||||||
|
info, err := os.Stat(filename)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false, nil
|
||||||
|
} else if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return !info.IsDir(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// load keys from non standard format
|
||||||
|
func LoadKeysIncompat(r io.Reader) (k I2PKeys, err error) {
|
||||||
|
var buff bytes.Buffer
|
||||||
|
_, err = io.Copy(&buff, r)
|
||||||
|
if err == nil {
|
||||||
|
parts := strings.Split(buff.String(), "\n")
|
||||||
|
k = I2PKeys{I2PAddr(parts[0]), parts[1]}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// load keys from non-standard format by specifying a text file.
|
||||||
|
// If the file does not exist, generate keys, otherwise, fail
|
||||||
|
// closed.
|
||||||
|
func LoadKeys(r string) (I2PKeys, error) {
|
||||||
|
exists, err := fileExists(r)
|
||||||
|
if err != nil {
|
||||||
|
return I2PKeys{}, err
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
fi, err := os.Open(r)
|
||||||
|
if err != nil {
|
||||||
|
return I2PKeys{}, err
|
||||||
|
}
|
||||||
|
defer fi.Close()
|
||||||
|
return LoadKeysIncompat(fi)
|
||||||
|
}
|
||||||
|
return I2PKeys{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// store keys in non standard format
|
||||||
|
func StoreKeysIncompat(k I2PKeys, w io.Writer) (err error) {
|
||||||
|
_, err = io.WriteString(w, k.Address.Base64()+"\n"+k.Both)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func StoreKeys(k I2PKeys, r string) error {
|
||||||
|
fi, err := os.Open(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fi.Close()
|
||||||
|
return StoreKeysIncompat(k, fi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k I2PKeys) Network() string {
|
||||||
|
return k.Address.Network()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the public keys of the I2PKeys.
|
||||||
|
func (k I2PKeys) Addr() I2PAddr {
|
||||||
|
return k.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k I2PKeys) Public() crypto.PublicKey {
|
||||||
|
return k.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k I2PKeys) Private() []byte {
|
||||||
|
src := strings.Split(k.String(), k.Addr().String())[0]
|
||||||
|
var dest []byte
|
||||||
|
_, err := i2pB64enc.Decode(dest, []byte(src))
|
||||||
|
panic(err)
|
||||||
|
return dest
|
||||||
|
}
|
||||||
|
|
||||||
|
type SecretKey interface {
|
||||||
|
Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k I2PKeys) SecretKey() SecretKey {
|
||||||
|
var pk ed25519.PrivateKey = k.Private()
|
||||||
|
return pk
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k I2PKeys) PrivateKey() crypto.PrivateKey {
|
||||||
|
var pk ed25519.PrivateKey = k.Private()
|
||||||
|
_, err := pk.Sign(rand.Reader, []byte("nonsense"), crypto.Hash(0))
|
||||||
|
if err != nil {
|
||||||
|
//TODO: Elgamal, P256, P384, P512, GOST? keys?
|
||||||
|
}
|
||||||
|
return pk
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k I2PKeys) Ed25519PrivateKey() *ed25519.PrivateKey {
|
||||||
|
return k.SecretKey().(*ed25519.PrivateKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*func (k I2PKeys) ElgamalPrivateKey() *ed25519.PrivateKey {
|
||||||
|
return k.SecretKey().(*ed25519.PrivateKey)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
//func (k I2PKeys) Decrypt(rand io.Reader, msg []byte, opts crypto.DecrypterOpts) (plaintext []byte, err error) {
|
||||||
|
//return k.SecretKey().(*ed25519.PrivateKey).Decrypt(rand, msg, opts)
|
||||||
|
//}
|
||||||
|
|
||||||
|
func (k I2PKeys) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
||||||
|
return k.SecretKey().(*ed25519.PrivateKey).Sign(rand, digest, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the keys (both public and private), in I2Ps base64 format. Use this
|
||||||
|
// when you create sessions.
|
||||||
|
func (k I2PKeys) String() string {
|
||||||
|
return k.Both
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k I2PKeys) HostnameEntry(hostname string, opts crypto.SignerOpts) (string, error) {
|
||||||
|
sig, err := k.Sign(rand.Reader, []byte(hostname), opts)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(sig), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// I2PAddr represents an I2P destination, almost equivalent to an IP address.
|
||||||
|
// This is the humongously huge base64 representation of such an address, which
|
||||||
|
// really is just a pair of public keys and also maybe a certificate. (I2P hides
|
||||||
|
// the details of exactly what it is. Read the I2P specifications for more info.)
|
||||||
|
type I2PAddr string
|
||||||
|
|
||||||
|
// an i2p destination hash, the .b32.i2p address if you will
|
||||||
|
type I2PDestHash [32]byte
|
||||||
|
|
||||||
|
// create a desthash from a string b32.i2p address
|
||||||
|
func DestHashFromString(str string) (dhash I2PDestHash, err error) {
|
||||||
|
if strings.HasSuffix(str, ".b32.i2p") && len(str) == 60 {
|
||||||
|
// valid
|
||||||
|
_, err = i2pB32enc.Decode(dhash[:], []byte(str[:52]+"===="))
|
||||||
|
} else {
|
||||||
|
// invalid
|
||||||
|
err = errors.New("invalid desthash format")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get string representation of i2p dest hash(base32 version)
|
||||||
|
func (h I2PDestHash) String() string {
|
||||||
|
b32addr := make([]byte, 56)
|
||||||
|
i2pB32enc.Encode(b32addr, h[:])
|
||||||
|
return string(b32addr[:52]) + ".b32.i2p"
|
||||||
|
}
|
||||||
|
|
||||||
|
// get base64 representation of i2p dest sha256 hash(the 44-character one)
|
||||||
|
func (h I2PDestHash) Hash() string {
|
||||||
|
hash := sha256.New()
|
||||||
|
hash.Write(h[:])
|
||||||
|
digest := hash.Sum(nil)
|
||||||
|
buf := make([]byte, 44)
|
||||||
|
i2pB64enc.Encode(buf, digest)
|
||||||
|
return string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns "I2P"
|
||||||
|
func (h *I2PDestHash) Network() string {
|
||||||
|
return "I2P"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the base64 representation of the I2PAddr
|
||||||
|
func (a I2PAddr) Base64() string {
|
||||||
|
return string(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the I2P destination (base32-encoded)
|
||||||
|
func (a I2PAddr) String() string {
|
||||||
|
return string(a.Base32())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns "I2P"
|
||||||
|
func (a I2PAddr) Network() string {
|
||||||
|
return "I2P"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new I2P address from a base64-encoded string. Checks if the address
|
||||||
|
// addr is in correct format. (If you know for sure it is, use I2PAddr(addr).)
|
||||||
|
func NewI2PAddrFromString(addr string) (I2PAddr, error) {
|
||||||
|
if strings.HasSuffix(addr, ".i2p") {
|
||||||
|
if strings.HasSuffix(addr, ".b32.i2p") {
|
||||||
|
return I2PAddr(""), errors.New("cannot convert .b32.i2p to full destination")
|
||||||
|
}
|
||||||
|
// strip off .i2p if it's there
|
||||||
|
addr = addr[:len(addr)-4]
|
||||||
|
}
|
||||||
|
addr = strings.Trim(addr, "\t\n\r\f ")
|
||||||
|
// very basic check
|
||||||
|
if len(addr) > 4096 || len(addr) < 516 {
|
||||||
|
return I2PAddr(""), errors.New("Not an I2P address")
|
||||||
|
}
|
||||||
|
buf := make([]byte, i2pB64enc.DecodedLen(len(addr)))
|
||||||
|
if _, err := i2pB64enc.Decode(buf, []byte(addr)); err != nil {
|
||||||
|
return I2PAddr(""), errors.New("Address is not base64-encoded")
|
||||||
|
}
|
||||||
|
return I2PAddr(addr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FiveHundredAs() I2PAddr {
|
||||||
|
s := ""
|
||||||
|
for x := 0; x < 517; x++ {
|
||||||
|
s += "A"
|
||||||
|
}
|
||||||
|
r, _ := NewI2PAddrFromString(s)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new I2P address from a byte array. The inverse of ToBytes().
|
||||||
|
func NewI2PAddrFromBytes(addr []byte) (I2PAddr, error) {
|
||||||
|
if len(addr) > 4096 || len(addr) < 384 {
|
||||||
|
return I2PAddr(""), errors.New("Not an I2P address")
|
||||||
|
}
|
||||||
|
buf := make([]byte, i2pB64enc.EncodedLen(len(addr)))
|
||||||
|
i2pB64enc.Encode(buf, addr)
|
||||||
|
return I2PAddr(string(buf)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turns an I2P address to a byte array. The inverse of NewI2PAddrFromBytes().
|
||||||
|
func (addr I2PAddr) ToBytes() ([]byte, error) {
|
||||||
|
return i2pB64enc.DecodeString(string(addr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (addr I2PAddr) Bytes() []byte {
|
||||||
|
b, _ := addr.ToBytes()
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the *.b32.i2p address of the I2P address. It is supposed to be a
|
||||||
|
// somewhat human-manageable 64 character long pseudo-domain name equivalent of
|
||||||
|
// the 516+ characters long default base64-address (the I2PAddr format). It is
|
||||||
|
// not possible to turn the base32-address back into a usable I2PAddr without
|
||||||
|
// performing a Lookup(). Lookup only works if you are using the I2PAddr from
|
||||||
|
// which the b32 address was generated.
|
||||||
|
func (addr I2PAddr) Base32() (str string) {
|
||||||
|
return addr.DestHash().String() + addr.Port()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (addr I2PAddr) Port() (str string) {
|
||||||
|
if FakePort {
|
||||||
|
return ":8080"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (addr I2PAddr) DestHash() (h I2PDestHash) {
|
||||||
|
hash := sha256.New()
|
||||||
|
b, _ := addr.ToBytes()
|
||||||
|
hash.Write(b)
|
||||||
|
digest := hash.Sum(nil)
|
||||||
|
copy(h[:], digest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes any string into a *.b32.i2p human-readable I2P address. This makes no
|
||||||
|
// sense, unless "anything" is an I2P destination of some sort.
|
||||||
|
func Base32(anything string) string {
|
||||||
|
return I2PAddr(anything).Base32()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDestination(samaddr string, sigType ...string) (I2PKeys, error) {
|
||||||
|
if samaddr == "" {
|
||||||
|
samaddr = "127.0.0.1:7656"
|
||||||
|
}
|
||||||
|
client, err := goSam.NewClient(samaddr)
|
||||||
|
if err != nil {
|
||||||
|
return I2PKeys{}, err
|
||||||
|
}
|
||||||
|
var sigtmp string
|
||||||
|
if len(sigType) > 0 {
|
||||||
|
sigtmp = sigType[0]
|
||||||
|
}
|
||||||
|
pub, priv, err := client.NewDestination(sigtmp)
|
||||||
|
if err != nil {
|
||||||
|
return I2PKeys{}, err
|
||||||
|
}
|
||||||
|
addr, err := NewI2PAddrFromBytes([]byte(pub))
|
||||||
|
if err != nil {
|
||||||
|
return I2PKeys{}, err
|
||||||
|
}
|
||||||
|
keys := NewKeys(addr, priv+pub)
|
||||||
|
if err != nil {
|
||||||
|
return I2PKeys{}, err
|
||||||
|
}
|
||||||
|
return keys, nil
|
||||||
|
}
|
21
i2pkeys/I2PAddr_test.go
Normal file
21
i2pkeys/I2PAddr_test.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package i2pkeys
|
||||||
|
|
||||||
|
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)
|
||||||
|
keys, err := NewDestination("")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
t.Fail()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(keys.String())
|
||||||
|
}
|
BIN
i2plogo.png
BIN
i2plogo.png
Binary file not shown.
Before Width: | Height: | Size: 46 KiB |
296
index.html
296
index.html
@@ -1,296 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>
|
|
||||||
README
|
|
||||||
</title>
|
|
||||||
<meta name="author" content="eyedeekay" />
|
|
||||||
<meta name="description" content="sam3" />
|
|
||||||
<meta name="keywords" content="master" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="navbar">
|
|
||||||
<a href="#shownav">
|
|
||||||
Show navigation
|
|
||||||
</a>
|
|
||||||
<div id="shownav">
|
|
||||||
<div id="hidenav">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a href="index.html">
|
|
||||||
index
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<br>
|
|
||||||
<a href="#hidenav">
|
|
||||||
Hide Navigation
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<h1>
|
|
||||||
<a href="/">
|
|
||||||
README
|
|
||||||
</a>
|
|
||||||
</h1>
|
|
||||||
<h2>
|
|
||||||
!!IMPORTANT!!
|
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
In the next version, I’ll be moving the
|
|
||||||
<code>
|
|
||||||
i2pkeys
|
|
||||||
</code>
|
|
||||||
directory to it’s own repository
|
|
||||||
so I can avoid import cycle headaches. Please migrate to the new
|
|
||||||
<code>
|
|
||||||
i2pkeys
|
|
||||||
</code>
|
|
||||||
repository
|
|
||||||
before upgrading your sam3 dependencies. You can probably do this by running:
|
|
||||||
</p>
|
|
||||||
<pre><code>find . -name '*.go' -exec sed -i 's|github.com/eyedeekay/sam3/i2pkeys|github.com/eyedeekay/i2pkeys|g' {} \;
|
|
||||||
</code></pre>
|
|
||||||
<h1>
|
|
||||||
README
|
|
||||||
</h1>
|
|
||||||
<p>
|
|
||||||
go library for the I2P
|
|
||||||
<a href="https://geti2p.net/en/docs/api/samv3" rel="nofollow">
|
|
||||||
SAMv3.0
|
|
||||||
</a>
|
|
||||||
bridge, used to build anonymous/pseudonymous end-to-end encrypted sockets.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
This library is much better than ccondom (that use BOB), much more stable and much easier to maintain.
|
|
||||||
</p>
|
|
||||||
<h2>
|
|
||||||
Support/TODO
|
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
<strong>
|
|
||||||
What works:
|
|
||||||
</strong>
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Utils
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Resolving domain names to I2P destinations
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
.b32.i2p hashes
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Generating keys/i2p destinations
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Streaming
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
DialI2P() - Connecting to stuff in I2P
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Listen()/Accept() - Handling incomming connections
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Implements net.Conn and net.Listener
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Datagrams
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Implements net.PacketConn
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Raw datagrams
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Like datagrams, but without addresses
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p>
|
|
||||||
<strong>
|
|
||||||
Does not work:
|
|
||||||
</strong>
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Stream Forwarding
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Probably needs some real-world testing
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<h2>
|
|
||||||
Documentation
|
|
||||||
</h2>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Latest version-documentation:
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
set your GOPATH
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Enter
|
|
||||||
<code>
|
|
||||||
godoc -http=:8081
|
|
||||||
</code>
|
|
||||||
into your terminal and hit enter.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Goto
|
|
||||||
<a href="http://localhost:8081" rel="nofollow">
|
|
||||||
http://localhost:8081
|
|
||||||
</a>
|
|
||||||
, click packages, and navigate to sam3
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<h2>
|
|
||||||
Examples
|
|
||||||
</h2>
|
|
||||||
<pre><code>package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/eyedeekay/sam3"
|
|
||||||
"github.com/eyedeekay/sam3/i2pkeys"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
const yoursam = "127.0.0.1:7656" // sam bridge
|
|
||||||
|
|
||||||
func client(server i2pkeys.I2PAddr) {
|
|
||||||
sam, _ := sam3.NewSAM(yoursam)
|
|
||||||
keys, _ := sam.NewKeys()
|
|
||||||
stream, _ := sam.NewStreamSession("clientTun", keys, sam3.Options_Small)
|
|
||||||
fmt.Println("Client: Connecting to " + server.Base32())
|
|
||||||
conn, _ := stream.DialI2P(server)
|
|
||||||
conn.Write([]byte("Hello world!"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
sam, _ := NewSAM(yoursam)
|
|
||||||
keys, _ := sam.NewKeys()
|
|
||||||
stream, _ := sam.NewStreamSession("serverTun", keys, sam3.Options_Medium)
|
|
||||||
listener, _ := stream.Listen()
|
|
||||||
go client(keys.Addr())
|
|
||||||
conn, _ := listener.Accept()
|
|
||||||
buf := make([]byte, 4096)
|
|
||||||
n, _ := conn.Read(buf)
|
|
||||||
fmt.Println("Server received: " + string(buf[:n]))
|
|
||||||
}
|
|
||||||
</code></pre>
|
|
||||||
<p>
|
|
||||||
The above will write to the terminal:
|
|
||||||
</p>
|
|
||||||
<pre><code>Client: Connecting to zjnvfh4hs3et5vtz35ogwzrws26zvwkcad5uo5esecvg4qpk5b4a.b32.i2p
|
|
||||||
Server received: Hello world!
|
|
||||||
</code></pre>
|
|
||||||
<p>
|
|
||||||
Error handling was omitted in the above code for readability.
|
|
||||||
</p>
|
|
||||||
<h2>
|
|
||||||
Testing
|
|
||||||
</h2>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<code>
|
|
||||||
go test -tags=nettest
|
|
||||||
</code>
|
|
||||||
runs the whole suite (takes 90+ sec to perform!)
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<code>
|
|
||||||
go test -short
|
|
||||||
</code>
|
|
||||||
runs the shorter variant, does not connect to anything
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<h2>
|
|
||||||
License
|
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Public domain.
|
|
||||||
</p>
|
|
||||||
<h2>
|
|
||||||
Author
|
|
||||||
</h2>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Kalle Vedin
|
|
||||||
<code>
|
|
||||||
kalle.vedin@fripost.org
|
|
||||||
</code>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Unknown Name (majestrate)
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
idk
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
qiwenmin
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div>
|
|
||||||
<a href="#show">
|
|
||||||
Show license
|
|
||||||
</a>
|
|
||||||
<div id="show">
|
|
||||||
<div id="hide">
|
|
||||||
<pre><code>This is free and unencumbered software released into the public domain.
|
|
||||||
|
|
||||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
distribute this software, either in source code form or as a compiled
|
|
||||||
binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
means.
|
|
||||||
|
|
||||||
In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
of this software dedicate any and all copyright interest in the
|
|
||||||
software to the public domain. We make this dedication for the benefit
|
|
||||||
of the public at large and to the detriment of our heirs and
|
|
||||||
successors. We intend this dedication to be an overt act of
|
|
||||||
relinquishment in perpetuity of all present and future rights to this
|
|
||||||
software under copyright law.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
||||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
||||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
For more information, please refer to <http://unlicense.org/>
|
|
||||||
</code></pre>
|
|
||||||
<a href="#hide">
|
|
||||||
Hide license
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<iframe src="https://snowflake.torproject.org/embed.html" width="320" height="240" frameborder="0" scrolling="no"></iframe>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a href="https://geti2p.net/">
|
|
||||||
<img src="i2plogo.png"></img>
|
|
||||||
I2P
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
110
primary.go
110
primary.go
@@ -9,7 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/eyedeekay/i2pkeys"
|
"github.com/eyedeekay/sam3/i2pkeys"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -33,8 +33,8 @@ type PrimarySession struct {
|
|||||||
Deadline time.Time
|
Deadline time.Time
|
||||||
sigType string
|
sigType string
|
||||||
Config SAMEmit
|
Config SAMEmit
|
||||||
stsess map[string]*StreamSession
|
stsess *StreamSession
|
||||||
dgsess map[string]*DatagramSession
|
dgsess *DatagramSession
|
||||||
// from string
|
// from string
|
||||||
// to string
|
// to string
|
||||||
}
|
}
|
||||||
@@ -62,10 +62,12 @@ func (ss *PrimarySession) Close() error {
|
|||||||
|
|
||||||
// Returns the I2P destination (the address) of the stream session
|
// Returns the I2P destination (the address) of the stream session
|
||||||
func (ss *PrimarySession) Addr() i2pkeys.I2PAddr {
|
func (ss *PrimarySession) Addr() i2pkeys.I2PAddr {
|
||||||
|
// fmt.Println("LOCAL ADDR")
|
||||||
return ss.keys.Addr()
|
return ss.keys.Addr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *PrimarySession) LocalAddr() net.Addr {
|
func (ss *PrimarySession) LocalAddr() net.Addr {
|
||||||
|
// fmt.Println("LOCAL ADDR")
|
||||||
aa := ss.keys.Addr()
|
aa := ss.keys.Addr()
|
||||||
return &aa
|
return &aa
|
||||||
}
|
}
|
||||||
@@ -77,11 +79,9 @@ func (ss *PrimarySession) Keys() i2pkeys.I2PKeys {
|
|||||||
|
|
||||||
func (sam *PrimarySession) Dial(network, addr string) (net.Conn, error) {
|
func (sam *PrimarySession) Dial(network, addr string) (net.Conn, error) {
|
||||||
if network == "udp" || network == "udp4" || network == "udp6" {
|
if network == "udp" || network == "udp4" || network == "udp6" {
|
||||||
//return sam.DialUDPI2P(network, network+addr[0:4], addr)
|
|
||||||
return sam.DialUDPI2P(network, network+addr[0:4], addr)
|
return sam.DialUDPI2P(network, network+addr[0:4], addr)
|
||||||
}
|
}
|
||||||
if network == "tcp" || network == "tcp4" || network == "tcp6" {
|
if network == "tcp" || network == "tcp4" || network == "tcp6" {
|
||||||
//return sam.DialTCPI2P(network, network+addr[0:4], addr)
|
|
||||||
return sam.DialTCPI2P(network, network+addr[0:4], addr)
|
return sam.DialTCPI2P(network, network+addr[0:4], addr)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Error: Must specify a valid network type")
|
return nil, fmt.Errorf("Error: Must specify a valid network type")
|
||||||
@@ -89,65 +89,59 @@ func (sam *PrimarySession) Dial(network, addr string) (net.Conn, error) {
|
|||||||
|
|
||||||
// DialTCP implements x/dialer
|
// DialTCP implements x/dialer
|
||||||
func (sam *PrimarySession) DialTCP(network string, laddr, raddr net.Addr) (net.Conn, error) {
|
func (sam *PrimarySession) DialTCP(network string, laddr, raddr net.Addr) (net.Conn, error) {
|
||||||
ts, ok := sam.stsess[network+raddr.String()[0:4]]
|
|
||||||
var err error
|
var err error
|
||||||
if !ok {
|
if sam.stsess == nil {
|
||||||
ts, err = sam.NewUniqueStreamSubSession(network + raddr.String()[0:4])
|
sam.stsess, err = sam.NewUniqueStreamSubSession(network + RandString())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sam.stsess[network+raddr.String()[0:4]] = ts
|
|
||||||
ts, _ = sam.stsess[network+raddr.String()[0:4]]
|
|
||||||
}
|
}
|
||||||
return ts.Dial(network, raddr.String())
|
return sam.stsess.Dial(network, raddr.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sam *PrimarySession) DialTCPI2P(network string, laddr, raddr string) (net.Conn, error) {
|
func (sam *PrimarySession) DialTCPI2P(network string, laddr, raddr string) (*SAMConn, error) {
|
||||||
ts, ok := sam.stsess[network+raddr[0:4]]
|
|
||||||
var err error
|
var err error
|
||||||
if !ok {
|
if sam.stsess == nil {
|
||||||
ts, err = sam.NewUniqueStreamSubSession(network + laddr)
|
sam.stsess, err = sam.NewUniqueStreamSubSession(network + RandString())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sam.stsess[network+raddr[0:4]] = ts
|
|
||||||
ts, _ = sam.stsess[network+raddr[0:4]]
|
|
||||||
}
|
}
|
||||||
return ts.Dial(network, raddr)
|
c, err := sam.stsess.Dial(network, raddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.(*SAMConn), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialUDP implements x/dialer
|
// DialUDP implements x/dialer
|
||||||
func (sam *PrimarySession) DialUDP(network string, laddr, raddr net.Addr) (net.PacketConn, error) {
|
func (sam *PrimarySession) DialUDP(network string, laddr, raddr net.Addr) (net.PacketConn, error) {
|
||||||
ds, ok := sam.dgsess[network+raddr.String()[0:4]]
|
|
||||||
var err error
|
var err error
|
||||||
if !ok {
|
if sam.dgsess == nil {
|
||||||
ds, err = sam.NewDatagramSubSession(network+raddr.String()[0:4], 0)
|
sam.dgsess, err = sam.NewDatagramSubSession(network+raddr.String()[0:4], 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sam.dgsess[network+raddr.String()[0:4]] = ds
|
|
||||||
ds, _ = sam.dgsess[network+raddr.String()[0:4]]
|
|
||||||
}
|
}
|
||||||
return ds.Dial(network, raddr.String())
|
return sam.dgsess.Dial(network, raddr.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sam *PrimarySession) DialUDPI2P(network, laddr, raddr string) (*DatagramSession, error) {
|
func (sam *PrimarySession) DialUDPI2P(network, laddr, raddr string) (*DatagramSession, error) {
|
||||||
ds, ok := sam.dgsess[network+raddr[0:4]]
|
|
||||||
var err error
|
var err error
|
||||||
if !ok {
|
if sam.dgsess == nil {
|
||||||
ds, err = sam.NewDatagramSubSession(network+laddr, 0)
|
sam.dgsess, err = sam.NewDatagramSubSession(network+raddr[0:4], 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sam.dgsess[network+raddr[0:4]] = ds
|
|
||||||
ds, _ = sam.dgsess[network+raddr[0:4]]
|
|
||||||
}
|
}
|
||||||
return ds.Dial(network, raddr)
|
return sam.dgsess.Dial(network, raddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PrimarySession) Lookup(name string) (a net.Addr, err error) {
|
func (s *PrimarySession) Lookup(name string) (a net.Addr, err error) {
|
||||||
var sam *SAM
|
var sam *SAM
|
||||||
name = strings.Split(name, ":")[0]
|
if len(strings.Split(name, ":")) <= 1 {
|
||||||
|
name += ":80"
|
||||||
|
}
|
||||||
sam, err = NewSAM(s.samAddr)
|
sam, err = NewSAM(s.samAddr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
defer sam.Close()
|
defer sam.Close()
|
||||||
@@ -157,43 +151,38 @@ func (s *PrimarySession) Lookup(name string) (a net.Addr, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sam *PrimarySession) Resolve(network, addr string) (net.Addr, error) {
|
func (sam *PrimarySession) Resolve(network, addr string) (net.Addr, error) {
|
||||||
|
fmt.Println("LOGGING RESOLUTION", network, addr)
|
||||||
return sam.Lookup(addr)
|
return sam.Lookup(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sam *PrimarySession) ResolveTCPAddr(network, dest string) (net.Addr, error) {
|
func (sam *PrimarySession) ResolveTCPAddr(network, dest string) (net.Addr, error) {
|
||||||
|
fmt.Println("LOGGING RESOLUTION", network, dest)
|
||||||
return sam.Lookup(dest)
|
return sam.Lookup(dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sam *PrimarySession) ResolveUDPAddr(network, dest string) (net.Addr, error) {
|
func (sam *PrimarySession) ResolveUDPAddr(network, dest string) (net.Addr, error) {
|
||||||
|
fmt.Println("LOGGING RESOLUTION", network, dest)
|
||||||
return sam.Lookup(dest)
|
return sam.Lookup(dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
return sam.newPrimarySession(PrimarySessionSwitch, id, keys, options)
|
conn, err := sam.newGenericSession("PRIMARY", id, keys, options, []string{})
|
||||||
}
|
|
||||||
|
|
||||||
func (sam *SAM) newPrimarySession(primarySessionSwitch string, id string, keys i2pkeys.I2PKeys, options []string) (*PrimarySession, error) {
|
|
||||||
conn, err := sam.newGenericSession(primarySessionSwitch, id, keys, options, []string{})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ssesss := make(map[string]*StreamSession)
|
return &PrimarySession{sam.Config.I2PConfig.Sam(), id, conn, keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, sam.Config, nil, nil}, nil
|
||||||
dsesss := make(map[string]*DatagramSession)
|
|
||||||
return &PrimarySession{sam.Config.I2PConfig.Sam(), id, conn, keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, sam.Config, ssesss, dsesss}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new PrimarySession with the I2CP- and PRIMARYinglib options as
|
// Creates a new PrimarySession with the I2CP- and PRIMARYinglib 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) NewPrimarySessionWithSignature(id string, keys i2pkeys.I2PKeys, options []string, sigType string) (*PrimarySession, error) {
|
func (sam *SAM) NewPrimarySessionWithSignature(id string, keys i2pkeys.I2PKeys, options []string, sigType string) (*PrimarySession, error) {
|
||||||
conn, err := sam.newGenericSessionWithSignature(PrimarySessionSwitch, id, keys, sigType, options, []string{})
|
conn, err := sam.newGenericSessionWithSignature("PRIMARY", id, keys, sigType, options, []string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ssesss := make(map[string]*StreamSession)
|
return &PrimarySession{sam.Config.I2PConfig.Sam(), id, conn, keys, time.Duration(600 * time.Second), time.Now(), sigType, sam.Config, nil, nil}, nil
|
||||||
dsesss := make(map[string]*DatagramSession)
|
|
||||||
return &PrimarySession{sam.Config.I2PConfig.Sam(), id, conn, keys, time.Duration(600 * time.Second), time.Now(), sigType, sam.Config, ssesss, dsesss}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new session with the style of either "STREAM", "DATAGRAM" or "RAW",
|
// Creates a new session with the style of either "STREAM", "DATAGRAM" or "RAW",
|
||||||
@@ -219,13 +208,13 @@ func (sam *PrimarySession) newGenericSubSessionWithSignatureAndPorts(style, id,
|
|||||||
conn := sam.conn
|
conn := sam.conn
|
||||||
fp := ""
|
fp := ""
|
||||||
tp := ""
|
tp := ""
|
||||||
if from != "0" && from != "" {
|
if from != "0" {
|
||||||
fp = " FROM_PORT=" + from
|
fp = " FROM_PORT=" + from
|
||||||
}
|
}
|
||||||
if to != "0" && to != "" {
|
if to != "0" {
|
||||||
tp = " TO_PORT=" + to
|
tp = " TO_PORT=" + to
|
||||||
}
|
}
|
||||||
scmsg := []byte("SESSION ADD STYLE=" + style + " ID=" + id + fp + tp + " " + strings.Join(extras, " ") + "\n")
|
scmsg := []byte("SESSION ADD STYLE=" + style + fp + tp + " ID=" + id + " " + strings.Join(extras, " ") + "\n")
|
||||||
for m, i := 0, 0; m != len(scmsg); i++ {
|
for m, i := 0, 0; m != len(scmsg); i++ {
|
||||||
if i == 15 {
|
if i == 15 {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
@@ -245,7 +234,6 @@ func (sam *PrimarySession) newGenericSubSessionWithSignatureAndPorts(style, id,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
text := string(buf[:n])
|
text := string(buf[:n])
|
||||||
//log.Println("SAM:", text)
|
|
||||||
if strings.HasPrefix(text, session_ADDOK) {
|
if strings.HasPrefix(text, session_ADDOK) {
|
||||||
//if sam.keys.String() != text[len(session_ADDOK):len(text)-1] {
|
//if sam.keys.String() != text[len(session_ADDOK):len(text)-1] {
|
||||||
//conn.Close()
|
//conn.Close()
|
||||||
@@ -260,7 +248,7 @@ func (sam *PrimarySession) newGenericSubSessionWithSignatureAndPorts(style, id,
|
|||||||
return nil, errors.New("Duplicate destination")
|
return nil, errors.New("Duplicate destination")
|
||||||
} else if text == session_INVALID_KEY {
|
} else if text == session_INVALID_KEY {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return nil, errors.New("Invalid key - Primary Session")
|
return nil, errors.New("Invalid key")
|
||||||
} else if strings.HasPrefix(text, session_I2P_ERROR) {
|
} else if strings.HasPrefix(text, session_I2P_ERROR) {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return nil, errors.New("I2P error " + text[len(session_I2P_ERROR):])
|
return nil, errors.New("I2P error " + text[len(session_I2P_ERROR):])
|
||||||
@@ -273,21 +261,23 @@ func (sam *PrimarySession) newGenericSubSessionWithSignatureAndPorts(style, id,
|
|||||||
// Creates a new StreamSession with the I2CP- and streaminglib options as
|
// Creates a new StreamSession 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 *PrimarySession) NewStreamSubSession(id string) (*StreamSession, error) {
|
func (sam *PrimarySession) NewStreamSubSession(id string) (*StreamSession, error) {
|
||||||
conn, err := sam.newGenericSubSession("STREAM", id, []string{})
|
s := RandString()
|
||||||
|
conn, err := sam.newGenericSubSession("STREAM", id+s, []string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &StreamSession{sam.Config.I2PConfig.Sam(), id, conn, sam.keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, "0", "0"}, nil
|
return &StreamSession{sam.Config.I2PConfig.Sam(), id + s, conn, sam.keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, "0", "0"}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new StreamSession with the I2CP- and streaminglib options as
|
// Creates a new StreamSession 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 *PrimarySession) NewUniqueStreamSubSession(id string) (*StreamSession, error) {
|
func (sam *PrimarySession) NewUniqueStreamSubSession(id string) (*StreamSession, error) {
|
||||||
conn, err := sam.newGenericSubSession("STREAM", id, []string{})
|
s := RandString()
|
||||||
|
conn, err := sam.newGenericSubSession("STREAM", id+s, []string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &StreamSession{sam.Config.I2PConfig.Sam(), id, conn, sam.keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, randport(), randport()}, nil
|
return &StreamSession{sam.Config.I2PConfig.Sam(), id + s, conn, sam.keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, randport(), "0"}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new StreamSession with the I2CP- and streaminglib options as
|
// Creates a new StreamSession with the I2CP- and streaminglib options as
|
||||||
@@ -319,7 +309,7 @@ func (s *PrimarySession) NewDatagramSubSession(id string, udpPort int) (*Datagra
|
|||||||
if udpPort == 0 {
|
if udpPort == 0 {
|
||||||
udpPort = 7655
|
udpPort = 7655
|
||||||
}
|
}
|
||||||
lhost, _, err := SplitHostPort(s.conn.LocalAddr().String())
|
lhost, _, err := net.SplitHostPort(s.conn.LocalAddr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Close()
|
s.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -332,7 +322,7 @@ func (s *PrimarySession) NewDatagramSubSession(id string, udpPort int) (*Datagra
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rhost, _, err := SplitHostPort(s.conn.RemoteAddr().String())
|
rhost, _, err := net.SplitHostPort(s.conn.RemoteAddr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Close()
|
s.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -342,10 +332,6 @@ func (s *PrimarySession) NewDatagramSubSession(id string, udpPort int) (*Datagra
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
|
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
|
||||||
if err != nil {
|
|
||||||
s.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
conn, err := s.newGenericSubSession("DATAGRAM", id, []string{"PORT=" + lport})
|
conn, err := s.newGenericSubSession("DATAGRAM", id, []string{"PORT=" + lport})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -362,7 +348,7 @@ func (s *PrimarySession) NewRawSubSession(id string, udpPort int) (*RawSession,
|
|||||||
if udpPort == 0 {
|
if udpPort == 0 {
|
||||||
udpPort = 7655
|
udpPort = 7655
|
||||||
}
|
}
|
||||||
lhost, _, err := SplitHostPort(s.conn.LocalAddr().String())
|
lhost, _, err := net.SplitHostPort(s.conn.LocalAddr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Close()
|
s.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -375,7 +361,7 @@ func (s *PrimarySession) NewRawSubSession(id string, udpPort int) (*RawSession,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rhost, _, err := SplitHostPort(s.conn.RemoteAddr().String())
|
rhost, _, err := net.SplitHostPort(s.conn.RemoteAddr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Close()
|
s.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -385,10 +371,6 @@ func (s *PrimarySession) NewRawSubSession(id string, udpPort int) (*RawSession,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
|
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
|
||||||
if err != nil {
|
|
||||||
s.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// conn, err := s.newGenericSubSession("RAW", id, s.keys, options, []string{"PORT=" + lport})
|
// conn, err := s.newGenericSubSession("RAW", id, s.keys, options, []string{"PORT=" + lport})
|
||||||
conn, err := s.newGenericSubSession("RAW", id, []string{"PORT=" + lport})
|
conn, err := s.newGenericSubSession("RAW", id, []string{"PORT=" + lport})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
// +build nettest
|
||||||
|
|
||||||
package sam3
|
package sam3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
|
// +build nettest
|
||||||
|
|
||||||
package sam3
|
package sam3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
"github.com/eyedeekay/sam3/i2pkeys"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_PrimaryStreamingDial(t *testing.T) {
|
func Test_PrimaryStreamingDial(t *testing.T) {
|
||||||
@@ -97,31 +98,27 @@ func Test_PrimaryStreamingServerClient(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer sam.Close()
|
defer sam.Close()
|
||||||
fmt.Println("\tServer: Creating tunnel")
|
fmt.Println("\tServer: Creating tunnel")
|
||||||
ss, err := sam.NewUniqueStreamSubSession("PrimaryServerClientTunnel")
|
ss, err := sam.NewStreamSubSession("primaryExampleServerTun")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer ss.Close()
|
defer ss.Close()
|
||||||
time.Sleep(time.Second * 10)
|
|
||||||
c, w := make(chan bool), make(chan bool)
|
c, w := make(chan bool), make(chan bool)
|
||||||
go func(c, w chan (bool)) {
|
go func(c, w chan (bool)) {
|
||||||
if !(<-w) {
|
if !(<-w) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
/*
|
/*sam2, err := NewSAM(yoursam)
|
||||||
sam2, err := NewSAM(yoursam)
|
if err != nil {
|
||||||
if err != nil {
|
c <- false
|
||||||
c <- false
|
return
|
||||||
return
|
}
|
||||||
}
|
defer sam2.Close()
|
||||||
defer sam2.Close()
|
keys, err := sam2.NewKeys()
|
||||||
keys, err := sam2.NewKeys()
|
if err != nil {
|
||||||
if err != nil {
|
c <- false
|
||||||
c <- false
|
return
|
||||||
return
|
}*/
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
fmt.Println("\tClient: Creating tunnel")
|
fmt.Println("\tClient: Creating tunnel")
|
||||||
ss2, err := sam.NewStreamSubSession("primaryExampleClientTun")
|
ss2, err := sam.NewStreamSubSession("primaryExampleClientTun")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -185,13 +182,26 @@ func ExamplePrimaryStreamSession() {
|
|||||||
return
|
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"})
|
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 {
|
if err != nil {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer sam.Close()
|
defer sam.Close()
|
||||||
conn, err := sam.Dial("tcp", "idk.i2p") //someone.Base32())
|
// See the example Option_* variables.
|
||||||
|
ss, err := sam.NewStreamSubSession("stream_example")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ss.Close()
|
||||||
|
someone, err := earlysam.Lookup("idk.i2p")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := ss.DialI2P(someone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
fmt.Println(err.Error())
|
||||||
return
|
return
|
||||||
@@ -211,6 +221,8 @@ func ExamplePrimaryStreamSession() {
|
|||||||
fmt.Println("Read HTTP/HTML from idk.i2p")
|
fmt.Println("Read HTTP/HTML from idk.i2p")
|
||||||
log.Println("Read HTTP/HTML from idk.i2p")
|
log.Println("Read HTTP/HTML from idk.i2p")
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
//Sending HTTP GET /
|
//Sending HTTP GET /
|
||||||
//Read HTTP/HTML from idk.i2p
|
//Read HTTP/HTML from idk.i2p
|
||||||
@@ -223,86 +235,73 @@ func ExamplePrimaryStreamListener() {
|
|||||||
|
|
||||||
const samBridge = "127.0.0.1:7656"
|
const samBridge = "127.0.0.1:7656"
|
||||||
|
|
||||||
var ss *StreamSession
|
earlysam, err := NewSAM(yoursam)
|
||||||
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 {
|
if err != nil {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer latesam.Close()
|
defer earlysam.Close()
|
||||||
keys2, err := latesam.NewKeys()
|
keys, err := earlysam.NewKeys()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
return
|
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"})
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
quit := make(chan bool)
|
||||||
|
|
||||||
|
// Client connecting to the server
|
||||||
|
go func(server i2pkeys.I2PAddr) {
|
||||||
|
cs, err := sam.NewStreamSubSession("client_example")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
quit <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer cs.Close()
|
||||||
|
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.NewStreamSubSession("server_example")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
fmt.Println(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer sc.Close()
|
defer ss.Close()
|
||||||
client := http.Client{
|
l, err := ss.Listen()
|
||||||
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 {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
fmt.Println(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
conn, err := l.Accept()
|
||||||
r, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
fmt.Println(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Println("Got response: " + string(r))
|
conn.Write([]byte("Hello world!"))
|
||||||
|
|
||||||
|
<-quit // waits for client to die, for example only
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Got response: Hello world!
|
//Hello world!
|
||||||
}
|
|
||||||
|
|
||||||
type exitHandler struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *exitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Write([]byte("Hello world!"))
|
|
||||||
}
|
}
|
||||||
|
6
raw.go
6
raw.go
@@ -7,7 +7,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/eyedeekay/i2pkeys"
|
"github.com/eyedeekay/sam3/i2pkeys"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The RawSession provides no authentication of senders, and there is no sender
|
// The RawSession provides no authentication of senders, and there is no sender
|
||||||
@@ -34,7 +34,7 @@ func (s *SAM) NewRawSession(id string, keys i2pkeys.I2PKeys, options []string, u
|
|||||||
if udpPort == 0 {
|
if udpPort == 0 {
|
||||||
udpPort = 7655
|
udpPort = 7655
|
||||||
}
|
}
|
||||||
lhost, _, err := SplitHostPort(s.conn.LocalAddr().String())
|
lhost, _, err := net.SplitHostPort(s.conn.LocalAddr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Close()
|
s.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -47,7 +47,7 @@ func (s *SAM) NewRawSession(id string, keys i2pkeys.I2PKeys, options []string, u
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rhost, _, err := SplitHostPort(s.conn.RemoteAddr().String())
|
rhost, _, err := net.SplitHostPort(s.conn.RemoteAddr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Close()
|
s.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@@ -6,7 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/eyedeekay/i2pkeys"
|
"github.com/eyedeekay/sam3/i2pkeys"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SAMResolver struct {
|
type SAMResolver struct {
|
||||||
@@ -55,7 +55,7 @@ func (sam *SAMResolver) Resolve(name string) (i2pkeys.I2PAddr, error) {
|
|||||||
if text == "RESULT=OK" {
|
if text == "RESULT=OK" {
|
||||||
continue
|
continue
|
||||||
} else if text == "RESULT=INVALID_KEY" {
|
} else if text == "RESULT=INVALID_KEY" {
|
||||||
errStr += "Invalid key - resolver."
|
errStr += "Invalid key."
|
||||||
} else if text == "RESULT=KEY_NOT_FOUND" {
|
} else if text == "RESULT=KEY_NOT_FOUND" {
|
||||||
errStr += "Unable to resolve " + name
|
errStr += "Unable to resolve " + name
|
||||||
} else if text == "NAME="+name {
|
} else if text == "NAME="+name {
|
||||||
|
8
sam3.go
8
sam3.go
@@ -11,9 +11,11 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/eyedeekay/i2pkeys"
|
"github.com/eyedeekay/sam3/i2pkeys"
|
||||||
|
)
|
||||||
|
|
||||||
. "github.com/eyedeekay/i2pkeys"
|
import (
|
||||||
|
. "github.com/eyedeekay/sam3/i2pkeys"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Used for controlling I2Ps SAMv3.
|
// Used for controlling I2Ps SAMv3.
|
||||||
@@ -257,7 +259,7 @@ func (sam *SAM) newGenericSessionWithSignatureAndPorts(style, id, from, to strin
|
|||||||
return nil, errors.New("Duplicate destination")
|
return nil, errors.New("Duplicate destination")
|
||||||
} else if text == session_INVALID_KEY {
|
} else if text == session_INVALID_KEY {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return nil, errors.New("Invalid key - SAM session")
|
return nil, errors.New("Invalid key")
|
||||||
} else if strings.HasPrefix(text, session_I2P_ERROR) {
|
} else if strings.HasPrefix(text, session_I2P_ERROR) {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return nil, errors.New("I2P error " + text[len(session_I2P_ERROR):])
|
return nil, errors.New("I2P error " + text[len(session_I2P_ERROR):])
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
// +build nettest
|
||||||
|
|
||||||
package sam3
|
package sam3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
137
stream.go
137
stream.go
@@ -6,11 +6,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/eyedeekay/i2pkeys"
|
"github.com/eyedeekay/sam3/i2pkeys"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Represents a streaming session.
|
// Represents a streaming session.
|
||||||
@@ -26,18 +28,6 @@ type StreamSession struct {
|
|||||||
to string
|
to string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StreamSession) SetDeadline(t time.Time) error {
|
|
||||||
return s.conn.SetDeadline(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamSession) SetReadDeadline(t time.Time) error {
|
|
||||||
return s.conn.SetReadDeadline(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamSession) SetWriteDeadline(t time.Time) error {
|
|
||||||
return s.conn.SetWriteDeadline(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *StreamSession) From() string {
|
func (ss *StreamSession) From() string {
|
||||||
return ss.from
|
return ss.from
|
||||||
}
|
}
|
||||||
@@ -64,10 +54,6 @@ func (ss *StreamSession) Addr() i2pkeys.I2PAddr {
|
|||||||
return ss.keys.Addr()
|
return ss.keys.Addr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *StreamSession) LocalAddr() net.Addr {
|
|
||||||
return ss.keys.Addr()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the keys associated with the stream session
|
// Returns the keys associated with the stream session
|
||||||
func (ss *StreamSession) Keys() i2pkeys.I2PKeys {
|
func (ss *StreamSession) Keys() i2pkeys.I2PKeys {
|
||||||
return ss.keys
|
return ss.keys
|
||||||
@@ -108,7 +94,7 @@ func (s *StreamSession) Lookup(name string) (i2pkeys.I2PAddr, error) {
|
|||||||
sam, err := NewSAM(s.samAddr)
|
sam, err := NewSAM(s.samAddr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
addr, err := sam.Lookup(name)
|
addr, err := sam.Lookup(name)
|
||||||
defer sam.Close()
|
sam.Close()
|
||||||
return addr, err
|
return addr, err
|
||||||
}
|
}
|
||||||
return i2pkeys.I2PAddr(""), err
|
return i2pkeys.I2PAddr(""), err
|
||||||
@@ -177,19 +163,15 @@ func (s *StreamSession) Dial(n, addr string) (c net.Conn, err error) {
|
|||||||
|
|
||||||
var i2paddr i2pkeys.I2PAddr
|
var i2paddr i2pkeys.I2PAddr
|
||||||
var host string
|
var host string
|
||||||
host, _, err = SplitHostPort(addr)
|
host, _, err = net.SplitHostPort(addr)
|
||||||
//log.Println("Dialing:", host)
|
if err == nil {
|
||||||
if err = IgnorePortError(err); err == nil {
|
|
||||||
// check for name
|
// check for name
|
||||||
if strings.HasSuffix(host, ".b32.i2p") || strings.HasSuffix(host, ".i2p") {
|
if strings.HasSuffix(host, ".b32.i2p") || strings.HasSuffix(host, ".i2p") {
|
||||||
// name lookup
|
// name lookup
|
||||||
i2paddr, err = s.Lookup(host)
|
i2paddr, err = s.Lookup(host)
|
||||||
//log.Println("Lookup:", i2paddr, err)
|
|
||||||
} else {
|
} else {
|
||||||
// probably a destination
|
// probably a destination
|
||||||
i2paddr, err = i2pkeys.NewI2PAddrFromBytes([]byte(host))
|
i2paddr = i2pkeys.I2PAddr(host)
|
||||||
//i2paddr = i2pkeys.I2PAddr(host)
|
|
||||||
//log.Println("Destination:", i2paddr, err)
|
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return s.DialI2P(i2paddr)
|
return s.DialI2P(i2paddr)
|
||||||
@@ -234,7 +216,7 @@ func (s *StreamSession) DialI2P(addr i2pkeys.I2PAddr) (*SAMConn, error) {
|
|||||||
return nil, errors.New("I2P internal error")
|
return nil, errors.New("I2P internal error")
|
||||||
case "RESULT=INVALID_KEY":
|
case "RESULT=INVALID_KEY":
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return nil, errors.New("Invalid key - Stream Session")
|
return nil, errors.New("Invalid key")
|
||||||
case "RESULT=INVALID_ID":
|
case "RESULT=INVALID_ID":
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return nil, errors.New("Invalid tunnel ID")
|
return nil, errors.New("Invalid tunnel ID")
|
||||||
@@ -257,3 +239,106 @@ func (s *StreamSession) Listen() (*StreamListener, error) {
|
|||||||
laddr: s.keys.Addr(),
|
laddr: s.keys.Addr(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type StreamListener struct {
|
||||||
|
// parent stream session
|
||||||
|
session *StreamSession
|
||||||
|
// our session id
|
||||||
|
id string
|
||||||
|
// our local address for this sam socket
|
||||||
|
laddr i2pkeys.I2PAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *StreamListener) From() string {
|
||||||
|
return l.session.from
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *StreamListener) To() string {
|
||||||
|
return l.session.to
|
||||||
|
}
|
||||||
|
|
||||||
|
// get our address
|
||||||
|
// implements net.Listener
|
||||||
|
func (l *StreamListener) Addr() net.Addr {
|
||||||
|
return l.laddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// implements net.Listener
|
||||||
|
func (l *StreamListener) Close() error {
|
||||||
|
return l.session.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// implements net.Listener
|
||||||
|
func (l *StreamListener) Accept() (net.Conn, error) {
|
||||||
|
return l.AcceptI2P()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExtractPairString(input, value string) string {
|
||||||
|
parts := strings.Split(input, " ")
|
||||||
|
for _, part := range parts {
|
||||||
|
if strings.HasPrefix(part, value) {
|
||||||
|
kv := strings.SplitN(input, "=", 2)
|
||||||
|
if len(kv) == 2 {
|
||||||
|
return kv[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExtractPairInt(input, value string) int {
|
||||||
|
rv, err := strconv.Atoi(ExtractPairString(input, value))
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return rv
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExtractDest(input string) string {
|
||||||
|
return strings.Split(input, " ")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// accept a new inbound connection
|
||||||
|
func (l *StreamListener) AcceptI2P() (*SAMConn, error) {
|
||||||
|
s, err := NewSAM(l.session.samAddr)
|
||||||
|
if err == nil {
|
||||||
|
// we connected to sam
|
||||||
|
// send accept() command
|
||||||
|
_, err = io.WriteString(s.conn, "STREAM ACCEPT ID="+l.id+" SILENT=false\n")
|
||||||
|
// read reply
|
||||||
|
rd := bufio.NewReader(s.conn)
|
||||||
|
// read first line
|
||||||
|
line, err := rd.ReadString(10)
|
||||||
|
log.Println(line)
|
||||||
|
if err == nil {
|
||||||
|
if strings.HasPrefix(line, "STREAM STATUS RESULT=OK") {
|
||||||
|
// we gud read destination line
|
||||||
|
destline, err := rd.ReadString(10)
|
||||||
|
log.Println(destline)
|
||||||
|
if err == nil {
|
||||||
|
dest := ExtractDest(destline)
|
||||||
|
l.session.from = ExtractPairString(destline, "FROM_PORT")
|
||||||
|
l.session.to = ExtractPairString(destline, "TO_PORT")
|
||||||
|
// return wrapped connection
|
||||||
|
dest = strings.Trim(dest, "\n")
|
||||||
|
return &SAMConn{
|
||||||
|
laddr: l.laddr,
|
||||||
|
raddr: i2pkeys.I2PAddr(dest),
|
||||||
|
conn: s.conn,
|
||||||
|
}, nil
|
||||||
|
} else {
|
||||||
|
s.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.Close()
|
||||||
|
return nil, errors.New("invalid sam line: " + line)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
@@ -1,115 +0,0 @@
|
|||||||
package sam3
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/eyedeekay/i2pkeys"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StreamListener struct {
|
|
||||||
// parent stream session
|
|
||||||
session *StreamSession
|
|
||||||
// our session id
|
|
||||||
id string
|
|
||||||
// our local address for this sam socket
|
|
||||||
laddr i2pkeys.I2PAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *StreamListener) From() string {
|
|
||||||
return l.session.from
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *StreamListener) To() string {
|
|
||||||
return l.session.to
|
|
||||||
}
|
|
||||||
|
|
||||||
// get our address
|
|
||||||
// implements net.Listener
|
|
||||||
func (l *StreamListener) Addr() net.Addr {
|
|
||||||
return l.laddr
|
|
||||||
}
|
|
||||||
|
|
||||||
// implements net.Listener
|
|
||||||
func (l *StreamListener) Close() error {
|
|
||||||
return l.session.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// implements net.Listener
|
|
||||||
func (l *StreamListener) Accept() (net.Conn, error) {
|
|
||||||
return l.AcceptI2P()
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExtractPairString(input, value string) string {
|
|
||||||
parts := strings.Split(input, " ")
|
|
||||||
for _, part := range parts {
|
|
||||||
if strings.HasPrefix(part, value) {
|
|
||||||
kv := strings.SplitN(input, "=", 2)
|
|
||||||
if len(kv) == 2 {
|
|
||||||
return kv[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExtractPairInt(input, value string) int {
|
|
||||||
rv, err := strconv.Atoi(ExtractPairString(input, value))
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return rv
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExtractDest(input string) string {
|
|
||||||
return strings.Split(input, " ")[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// accept a new inbound connection
|
|
||||||
func (l *StreamListener) AcceptI2P() (*SAMConn, error) {
|
|
||||||
s, err := NewSAM(l.session.samAddr)
|
|
||||||
if err == nil {
|
|
||||||
// we connected to sam
|
|
||||||
// send accept() command
|
|
||||||
_, err = io.WriteString(s.conn, "STREAM ACCEPT ID="+l.id+" SILENT=false\n")
|
|
||||||
// read reply
|
|
||||||
rd := bufio.NewReader(s.conn)
|
|
||||||
// read first line
|
|
||||||
line, err := rd.ReadString(10)
|
|
||||||
log.Println(line)
|
|
||||||
if err == nil {
|
|
||||||
if strings.HasPrefix(line, "STREAM STATUS RESULT=OK") {
|
|
||||||
// we gud read destination line
|
|
||||||
destline, err := rd.ReadString(10)
|
|
||||||
if err == nil {
|
|
||||||
dest := ExtractDest(destline)
|
|
||||||
l.session.from = ExtractPairString(destline, "FROM_PORT")
|
|
||||||
l.session.to = ExtractPairString(destline, "TO_PORT")
|
|
||||||
// return wrapped connection
|
|
||||||
dest = strings.Trim(dest, "\n")
|
|
||||||
return &SAMConn{
|
|
||||||
laddr: l.laddr,
|
|
||||||
raddr: i2pkeys.I2PAddr(dest),
|
|
||||||
conn: s.conn,
|
|
||||||
}, nil
|
|
||||||
} else {
|
|
||||||
s.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s.Close()
|
|
||||||
return nil, errors.New("invalid sam line: " + line)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
@@ -1,3 +1,5 @@
|
|||||||
|
// +build nettest
|
||||||
|
|
||||||
package sam3
|
package sam3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -6,7 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/eyedeekay/i2pkeys"
|
"github.com/eyedeekay/sam3/i2pkeys"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_StreamingDial(t *testing.T) {
|
func Test_StreamingDial(t *testing.T) {
|
||||||
|
157
style.css
157
style.css
@@ -1,157 +0,0 @@
|
|||||||
/* edgar default CSS file */
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: "Roboto";
|
|
||||||
font-family: monospace;
|
|
||||||
text-align: justify;
|
|
||||||
background-color: #373636;
|
|
||||||
color: whitesmoke;
|
|
||||||
font-size: 1.15em;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
width: 55%;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol {
|
|
||||||
width: 55%;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-top: 1%;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
max-width: 90%;
|
|
||||||
margin-top: 1%;
|
|
||||||
margin-left: 3%;
|
|
||||||
margin-right: 3%;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
float: left;
|
|
||||||
top: 5%;
|
|
||||||
left: 5%;
|
|
||||||
max-width: 60%;
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inline {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link-button:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link-button:active {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
font-family: monospace;
|
|
||||||
border-radius: 5%;
|
|
||||||
padding: 1%;
|
|
||||||
border-color: darkgray;
|
|
||||||
font-size: .9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #C6D9FE;
|
|
||||||
padding: 1%;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul li {
|
|
||||||
color: #C6D9FE;
|
|
||||||
}
|
|
||||||
|
|
||||||
iframe {
|
|
||||||
background: aliceblue;
|
|
||||||
border-radius: 15%;
|
|
||||||
margin: 2%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
width: 36vw;
|
|
||||||
height: 64vh;
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-toolbar a {
|
|
||||||
display: inline-block;
|
|
||||||
text-align: center;
|
|
||||||
text-decoration: none !important;
|
|
||||||
color: whitesmoke !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#feed {
|
|
||||||
width: 60vw;
|
|
||||||
height: unset !important;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
float: right;
|
|
||||||
background-color: #373636;
|
|
||||||
color: whitesmoke;
|
|
||||||
border: #C6D9FE solid 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thread-post,
|
|
||||||
.thread {
|
|
||||||
color: whitesmoke !important;
|
|
||||||
background-color: #373636;
|
|
||||||
border: 1px solid darkgray;
|
|
||||||
font-size: inherit;
|
|
||||||
padding-top: 1%;
|
|
||||||
padding-bottom: 1%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thread-post {
|
|
||||||
margin-left: 4%;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
text-align: center;
|
|
||||||
color: whitesmoke !important;
|
|
||||||
background-color: #373636;
|
|
||||||
border: 1px solid darkgray;
|
|
||||||
font: normal normal normal 14px/1 FontAwesome;
|
|
||||||
font-size: inherit;
|
|
||||||
padding-top: 1%;
|
|
||||||
padding-bottom: 1%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thread-hash {
|
|
||||||
text-align: right;
|
|
||||||
color: whitesmoke !important;
|
|
||||||
background-color: #373636;
|
|
||||||
border: 1px solid darkgray;
|
|
||||||
font-size: inherit;
|
|
||||||
padding-top: 1%;
|
|
||||||
padding-bottom: 1%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-body {
|
|
||||||
text-align: left;
|
|
||||||
color: whitesmoke !important;
|
|
||||||
font-size: inherit;
|
|
||||||
padding-top: 1%;
|
|
||||||
padding-bottom: 1%;
|
|
||||||
}
|
|
||||||
#show {display:none; }
|
|
||||||
#hide {display:block; }
|
|
||||||
#show:target {display: block; }
|
|
||||||
#hide:target {display: none; }
|
|
||||||
|
|
||||||
#shownav {display:none; }
|
|
||||||
#hidenav {display:block; }
|
|
||||||
#shownav:target {display: block; }
|
|
||||||
#hidenav:target {display: none; }
|
|
||||||
|
|
||||||
#navbar {
|
|
||||||
float: right;
|
|
||||||
width: 10%;
|
|
||||||
}
|
|
@@ -1,11 +1,5 @@
|
|||||||
package sam3
|
package sam3
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Examples and suggestions for options when creating sessions.
|
// Examples and suggestions for options when creating sessions.
|
||||||
var (
|
var (
|
||||||
// Suitable options if you are shuffling A LOT of traffic. If unused, this
|
// Suitable options if you are shuffling A LOT of traffic. If unused, this
|
||||||
@@ -57,51 +51,3 @@ var (
|
|||||||
"inbound.backupQuantity=0", "outbound.backupQuantity=0",
|
"inbound.backupQuantity=0", "outbound.backupQuantity=0",
|
||||||
"inbound.quantity=2", "outbound.quantity=2"}
|
"inbound.quantity=2", "outbound.quantity=2"}
|
||||||
)
|
)
|
||||||
|
|
||||||
func PrimarySessionString() string {
|
|
||||||
_, err := http.Get("http://127.0.0.1:7070")
|
|
||||||
if err != nil {
|
|
||||||
_, err := http.Get("http://127.0.0.1:7657")
|
|
||||||
if err != nil {
|
|
||||||
return "MASTER"
|
|
||||||
}
|
|
||||||
// at this point we're probably running on Java I2P and thus probably
|
|
||||||
// have a PRIMARY session. Just to be sure, try to make one, check
|
|
||||||
// for errors, then immediately close it.
|
|
||||||
testSam, err := NewSAM(SAMDefaultAddr(""))
|
|
||||||
if err != nil {
|
|
||||||
return "MASTER"
|
|
||||||
}
|
|
||||||
newKeys, err := testSam.NewKeys()
|
|
||||||
if err != nil {
|
|
||||||
return "MASTER"
|
|
||||||
}
|
|
||||||
primarySession, err := testSam.newPrimarySession("PRIMARY", "primaryTestTunnel", newKeys, Options_Small)
|
|
||||||
if err != nil {
|
|
||||||
return "MASTER"
|
|
||||||
}
|
|
||||||
primarySession.Close()
|
|
||||||
return "PRIMARY"
|
|
||||||
}
|
|
||||||
return "MASTER"
|
|
||||||
}
|
|
||||||
|
|
||||||
var PrimarySessionSwitch string = PrimarySessionString()
|
|
||||||
|
|
||||||
func getEnv(key, fallback string) string {
|
|
||||||
value, ok := os.LookupEnv(key)
|
|
||||||
if !ok {
|
|
||||||
return fallback
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
var SAM_HOST = getEnv("sam_host", "127.0.0.1")
|
|
||||||
var SAM_PORT = getEnv("sam_port", "7656")
|
|
||||||
|
|
||||||
func SAMDefaultAddr(fallforward string) string {
|
|
||||||
if fallforward == "" {
|
|
||||||
return net.JoinHostPort(SAM_HOST, SAM_PORT)
|
|
||||||
}
|
|
||||||
return fallforward
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user