79 Commits

Author SHA1 Message Date
idk
ca8d8688cb Split key generation out in helper 2021-10-27 22:38:52 -04:00
idk
ef67dc1e44 Split key generation out in helper 2021-10-27 22:38:21 -04:00
idk
31b8d62f04 Allow passing either pointer or instance to DialI2PRemote in DatagramSession 2021-07-18 15:06:49 -04:00
idk
d191b3404e Allow passing either pointer or instance to DialI2PRemote in DatagramSession 2021-07-18 15:05:36 -04:00
idk
1de06ec9b9 Allow passing either pointer or instance to DialI2PRemote in DatagramSession 2021-07-18 15:05:02 -04:00
idk
ed814a2fc6 Add SetReadDeadline and SetWriteDeadline to StreamSession 2021-04-15 21:11:56 -04:00
idk
746084e65f Add localaddr to streamsession 2021-04-15 20:51:07 -04:00
idk
1a55eb6e90 Merge branch 'easy-keys' into 'master'
Support SAMv3.3 PRIMARY sessions and all 3 types of SubSessions

See merge request idk/sam3!1
2021-04-15 00:50:41 +00:00
idk
d307d85458 Actually, use SubSessions 2021-02-28 23:22:42 -05:00
idk
0be404e00f Actually, use SubSessions 2021-02-28 23:11:33 -05:00
idk
c37b1a099e Append a random string to dialed addrs 2021-02-28 22:54:54 -05:00
idk
9c93a9e934 Add a resolver 2021-02-28 18:47:37 -05:00
idk
a4c6ef983d implement x/dialer 2021-02-28 18:32:45 -05:00
idk
5629074653 implement x/dialer 2021-02-28 18:13:39 -05:00
idk
4e4d930c06 implement x/dialer 2021-02-28 17:00:17 -05:00
idk
18b8c77c79 Make it so that PrimarySession.Dial can be used like net.Dial 2021-02-27 00:02:04 -05:00
idk
aa8028230e Make it so that PrimarySession.Dial can be used like net.Dial 2021-02-26 23:52:54 -05:00
idk
6c5a389c9b Make it so that PrimarySession.Dial can be used like net.Dial 2021-02-26 23:50:56 -05:00
idk
818f18648a Primary Session should return LocalAddr 2021-02-26 16:04:18 -05:00
idk
ddbe66bfe9 Primary Session should return LocalAddr 2021-02-26 16:02:14 -05:00
idk
f46a953bc8 Primary Session should return LocalAddr 2021-02-26 16:00:43 -05:00
idk
e5f304f552 Primary Session should return LocalAddr 2021-02-26 15:55:22 -05:00
idk
27810c197c experiment: try using base32 as String in address since most things are going to expect it anyway, I think 2021-02-26 15:25:59 -05:00
idk
6c76e83617 experiment: try using base32 as String in address since most things are going to expect it anyway, I think 2021-02-26 13:09:39 -05:00
idk
1748849c87 experiment: try using base32 as String in address since most things are going to expect it anyway, I think 2021-02-26 13:07:22 -05:00
idk
0f604e88b2 Try directly creating a listener on top of a PrimarySession 2021-02-26 10:52:19 -05:00
idk
3ffa61bf70 Neat that was easy, SAM Primary sessions appear to work. 2021-02-22 12:54:52 -05:00
idk
e9ab98c71b Also port the datagram example to the Primary session 2021-02-21 00:28:26 -05:00
idk
6b90389d7f Add a basic test of a Primary session by making the datagramm session test fire off a datagram subsession 2021-02-21 00:07:06 -05:00
idk
0b4982e1fe Throw in some other useful defaults while I'm at it. TODO, switch everything over to the option emitter. Thought I already did that. 2021-02-19 23:38:05 -05:00
idk
06ef087039 That... might actually do it, but there's probably something important I missed so far. I'll write up primary_test.go tomorrow. 2021-02-19 23:32:05 -05:00
idk
2cb9b8f30c Add DatagramSession to helper library, stub out samv3.3 primary sessions 2021-02-19 21:47:37 -05:00
idk
bdc1edb224 Add DatagramSession to helper library, stub out samv3.3 primary sessions 2021-02-19 21:44:59 -05:00
idk
de74967cd2 Add DatagramSession to helper library, stub out samv3.3 primary sessions 2021-02-19 21:28:28 -05:00
idk
e8b472bed9 Ask for keys from the SAM API as a one-off event in i2pkeys. Set up for implementing crypto.Public/Private keys, crypto.Signer 2021-01-22 16:26:07 -05:00
idk
e45d6f7cb0 Make it so i2pkeys also implements net.Addr 2020-12-30 20:53:27 -05:00
idk
f3baac1fbe update mods and fmt 2020-12-14 21:43:28 -05:00
idk
692064fd13 simplify listener helper a bit 2020-12-14 21:43:04 -05:00
idk
109830a970 gofmt 2020-11-23 20:38:35 -05:00
idk
d8c42f6a0a Prep to cut a new release 2020-11-22 12:50:23 -05:00
idk
9f0d7f348c Prep to cut a new release 2020-11-22 12:48:28 -05:00
idk
f464873c93 add I2PStreamSession to helper 2020-11-22 00:08:55 -05:00
idk
a2f2e6786a add I2PStreamSession to helper 2020-11-22 00:05:04 -05:00
idk
704f2971b5 add a "helper" function which just magically creates a listener 2020-11-21 17:44:50 -05:00
idk
aef996d60a update deb changelog 2020-10-18 01:04:40 -04:00
idk
529cdd6063 update deb changelog 2020-10-18 01:03:01 -04:00
idk
33f3caa3f9 Add fmt make target, incorporate address length fix from qiwenmin, add a shortcut for saving your keys 2020-10-18 00:58:45 -04:00
idk
5a43ad260d Merge pull request #3 from qiwenmin/fix-wrong-addr
Fix wrong address.
2020-10-18 04:34:41 +00:00
Qi Wenmin
cd744bfe49 Fix wrong address.
With some addresses, ToBytes() calculates the wrong buffer lenngth
and returns the larger buffer for next step (sha256). So the b32
address will wrong.

For example, when the b64 key is the following:

tr2kQArAdxb7rt3Hr6itisY1Pi7ncWVUYDan2rjB45F1lIFvDPJih5dBa~WfWTvfyt
QKdtFoGHLuZdC8X71R5GXGvh8N0ECBtjhYPG8chFSEO2Cab2SZKYskEqGeOrlOvAQ-
yYaH0xCkarxFdvEbXoV~cfDhHDJcZI3Uc522sLWlB72-uOH9nlzyt-41byGc5GWn5c
CxJZx8UfszUIG9B2QoptrN0xquWvkLxescZ~0JYePTEJTeQFWlfPAiHcuAloAq3yKt
DCY4xcSracQr9ieCmnFJoUsLtzsjSrn7L~WwlI4pYYXxkm2dtzR8cPHJCBUBNMbK~K
TIxLtH0Tt9wI3edHgRMdxNm3EoozpPMXA05Ouni~ehWMhaywiJKTDZcGKDRqXJVm~y
xJGcXiXT2UzdwM~D20KTTUlLmeRqsU5hqpk2kgBIWKFfb8Gunx2hr0wEHsJypUwDQ-
FSGT0Fw0MRGq0QWvwXxroPbJdjsNnYOKCEogHxC4VrmzaLhHlEBQAEAAcAAA==

the right raw key length is 391, but ToBytes() returns a 393 bytes
raw key.

Using DecodeString() can fix this issue.
2020-09-27 08:33:52 +08:00
idk
1ef2d90f46 fix some debian stuff 2020-06-22 18:15:05 -04:00
idk
f90e3b2755 remove it from go mod 2020-06-22 14:09:44 -04:00
idk
a959ac58ba Merge pull request #2 from eyedeekay/fancy-config
Fancy config
2019-12-08 03:14:32 +00:00
idk
eecb73010c merge config parts of ramp 2019-12-07 22:10:55 -05:00
idk
442114200c purge ramp 2019-12-07 22:09:40 -05:00
idk
d4ac38fc97 purge ramp 2019-12-07 22:06:50 -05:00
idk
0a2d58418f Merge pull request #1 from eyedeekay/desthash44
add 44-char dest hashes
2019-12-07 22:31:43 +00:00
idk
ca1a4f7f54 update deb changelog 2019-12-07 17:31:04 -05:00
idk
6406efd277 add 44-char dest hashes 2019-12-07 17:26:28 -05:00
idk
a12f9ba498 add makefile to ease releases 2019-10-22 02:26:57 -04:00
idk
4a24538cd5 windows compatibility fixes 2019-10-22 02:24:38 -04:00
idk
f617388473 windows compatibility fixes 2019-10-22 02:19:01 -04:00
idk
c2b2b39c74 gofmt 2019-10-22 02:06:08 -04:00
idk
0067d37ca3 windows compatibility fixes 2019-10-22 02:05:58 -04:00
idk
f8d54526ea update go mod 2019-07-30 14:51:40 -04:00
idk
e9868b791e enable setting the write buffer size 2019-07-30 14:50:14 -04:00
idk
a810f009bd update modules 2019-07-29 20:02:43 -04:00
idk
99ad6522eb implement UDPConn as well 2019-06-12 23:41:17 -04:00
idk
6f6be01fb2 implement UDPConn too 2019-06-12 23:33:06 -04:00
idk
7438f855cd implement UDPConn too 2019-06-12 23:32:35 -04:00
idk
eabe42f33f completely remove I2PAddr.go to its own thing so it can be used separately 2019-05-25 14:36:22 -04:00
idk
0e9665027e completely remove I2PAddr.go to its own thing so it can be used separately 2019-05-25 14:36:16 -04:00
idk
79b526d69c test, fmt, update changelog and version number 2019-05-18 18:34:57 -04:00
idk
5b1dcc54aa Create stable branch 2019-05-18 18:32:21 -04:00
idk
6b863d7514 Create stable branch 2019-05-18 18:32:12 -04:00
idk
9ee74a89d3 Create stable branch 2019-05-18 18:31:44 -04:00
idk
6029d7dd6e Create stable branch 2019-05-18 18:30:22 -04:00
idk
265e8b21b4 update changelog 2019-05-18 18:21:01 -04:00
idk
9d24d56c28 thought that might have happened 2019-04-29 16:21:40 -04:00
idk
d622765b79 forgot to add moved i2pkeys dir 2019-04-20 14:50:55 -04:00
25 changed files with 2506 additions and 110 deletions

29
Makefile Normal file
View File

@@ -0,0 +1,29 @@
USER_GH=eyedeekay
VERSION=0.33.001
packagename=sam3
echo:
@echo "type make version to do release $(VERSION)"
version:
gothub release -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION) -d "version $(VERSION)"
del:
gothub delete -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION)
tar:
tar --exclude .git \
--exclude .go \
--exclude bin \
-cJvf ../$(packagename)_$(VERSION).orig.tar.xz .
copier:
echo '#! /usr/bin/env sh' > deb/copy.sh
echo 'for f in $$(ls); do scp $$f/*.deb user@192.168.99.106:~/DEBIAN_PKGS/$$f/main/; done' >> deb/copy.sh
fmt:
find . -name '*.go' -exec gofmt -w -s {} \;
upload-linux:
gothub upload -R -u $(USER_GH) -r "$(packagename)" -t $(VERSION) -l `sha256sum ` -n "$(packagename)" -f "$(packagename)"

View File

@@ -57,6 +57,8 @@ func client(server I2PAddr) {
func main() {
sam, _ := NewSAM(yoursam)
keys, _ := sam.NewKeys()
stream, _ := sam.NewStreamSession("serverTun", keys, Options_Medium)
listener, _ := stream.Listen()
go client(keys.Addr())
stream, _ := sam.NewStreamSession("serverTun", keys, Options_Medium)
listener, _ := stream.Listen()
@@ -78,7 +80,7 @@ Error handling was omitted in the above code for readability.
## Testing ##
* `go test` runs the whole suite (takes 90+ sec to perform!)
* `go test -tags=nettest` runs the whole suite (takes 90+ sec to perform!)
* `go test -short` runs the shorter variant, does not connect to anything
## License ##
@@ -88,4 +90,6 @@ Public domain.
## Author ##
* Kalle Vedin `kalle.vedin@fripost.org`
* Unknown Name (majestrate)
* Unknown Name (majestrate)
* idk
* qiwenmin

View File

@@ -1,18 +1,20 @@
package sam3
import (
"github.com/eyedeekay/sam3/i2pkeys"
"net"
"time"
)
/*
import (
. "github.com/eyedeekay/sam3/i2pkeys"
)
*/
// Implements net.Conn
type SAMConn struct {
laddr I2PAddr
raddr I2PAddr
laddr i2pkeys.I2PAddr
raddr i2pkeys.I2PAddr
conn net.Conn
}
@@ -38,7 +40,7 @@ func (sc *SAMConn) LocalAddr() net.Addr {
}
// Implements net.Conn
func (sc *SAMConn) localAddr() I2PAddr {
func (sc *SAMConn) localAddr() i2pkeys.I2PAddr {
return sc.laddr
}
@@ -47,7 +49,7 @@ func (sc *SAMConn) RemoteAddr() net.Addr {
}
// Implements net.Conn
func (sc *SAMConn) remoteAddr() I2PAddr {
func (sc *SAMConn) remoteAddr() i2pkeys.I2PAddr {
return sc.raddr
}

298
config.go
View File

@@ -2,15 +2,301 @@ package sam3
import (
"fmt"
"math/rand"
"net"
"strconv"
"strings"
"github.com/eyedeekay/sam3/i2pkeys"
)
import (
. "github.com/eyedeekay/sam3/i2pkeys"
)
// I2PConfig is a struct which manages I2P configuration options
type I2PConfig struct {
SamHost string
SamPort string
TunName string
// sam config
SamMin string
SamMax string
Fromport string
Toport string
Style string
TunType string
DestinationKeys i2pkeys.I2PKeys
SigType string
EncryptLeaseSet string
LeaseSetKey string
LeaseSetPrivateKey string
LeaseSetPrivateSigningKey string
LeaseSetKeys i2pkeys.I2PKeys
InAllowZeroHop string
OutAllowZeroHop string
InLength string
OutLength string
InQuantity string
OutQuantity string
InVariance string
OutVariance string
InBackupQuantity string
OutBackupQuantity string
FastRecieve string
UseCompression string
MessageReliability string
CloseIdle string
CloseIdleTime string
ReduceIdle string
ReduceIdleTime string
ReduceIdleQuantity string
//Streaming Library options
AccessListType string
AccessList []string
}
func (f *I2PConfig) Sam() string {
host := "127.0.0.1"
port := "7656"
if f.SamHost != "" {
host = f.SamHost
}
if f.SamPort != "" {
port = f.SamPort
}
return host + ":" + port
}
func (f *I2PConfig) SetSAMAddress(addr string) {
hp := strings.Split(addr, ":")
if len(hp) == 1 {
f.SamHost = hp[0]
} else if len(hp) == 2 {
f.SamPort = hp[1]
f.SamHost = hp[0]
}
f.SamPort = "7656"
f.SamHost = "127.0.0.1"
}
func (f *I2PConfig) ID() string {
if f.TunName == "" {
b := make([]byte, 12)
for i := range b {
b[i] = "abcdefghijklmnopqrstuvwxyz"[rand.Intn(len("abcdefghijklmnopqrstuvwxyz"))]
}
f.TunName = string(b)
}
return " ID=" + f.TunName + " "
}
func (f *I2PConfig) Leasesetsettings() (string, string, string) {
var r, s, t string
if f.LeaseSetKey != "" {
r = " i2cp.leaseSetKey=" + f.LeaseSetKey + " "
}
if f.LeaseSetPrivateKey != "" {
s = " i2cp.leaseSetPrivateKey=" + f.LeaseSetPrivateKey + " "
}
if f.LeaseSetPrivateSigningKey != "" {
t = " i2cp.leaseSetPrivateSigningKey=" + f.LeaseSetPrivateSigningKey + " "
}
return r, s, t
}
func (f *I2PConfig) FromPort() string {
if f.samMax() < 3.1 {
return ""
}
if f.Fromport != "0" {
return " FROM_PORT=" + f.Fromport + " "
}
return ""
}
func (f *I2PConfig) ToPort() string {
if f.samMax() < 3.1 {
return ""
}
if f.Toport != "0" {
return " TO_PORT=" + f.Toport + " "
}
return ""
}
func (f *I2PConfig) SessionStyle() string {
if f.Style != "" {
return " STYLE=" + f.Style + " "
}
return " STYLE=STREAM "
}
func (f *I2PConfig) samMax() float64 {
i, err := strconv.Atoi(f.SamMax)
if err != nil {
return 3.1
}
return float64(i)
}
func (f *I2PConfig) MinSAM() string {
if f.SamMin == "" {
return "3.0"
}
return f.SamMin
}
func (f *I2PConfig) MaxSAM() string {
if f.SamMax == "" {
return "3.1"
}
return f.SamMax
}
func (f *I2PConfig) DestinationKey() string {
if &f.DestinationKeys != nil {
return " DESTINATION=" + f.DestinationKeys.String() + " "
}
return " DESTINATION=TRANSIENT "
}
func (f *I2PConfig) SignatureType() string {
if f.samMax() < 3.1 {
return ""
}
if f.SigType != "" {
return " SIGNATURE_TYPE=" + f.SigType + " "
}
return ""
}
func (f *I2PConfig) EncryptLease() string {
if f.EncryptLeaseSet == "true" {
return " i2cp.encryptLeaseSet=true "
}
return ""
}
func (f *I2PConfig) Reliability() string {
if f.MessageReliability != "" {
return " i2cp.messageReliability=" + f.MessageReliability + " "
}
return ""
}
func (f *I2PConfig) Reduce() string {
if f.ReduceIdle == "true" {
return "i2cp.reduceOnIdle=" + f.ReduceIdle + "i2cp.reduceIdleTime=" + f.ReduceIdleTime + "i2cp.reduceQuantity=" + f.ReduceIdleQuantity
}
return ""
}
func (f *I2PConfig) Close() string {
if f.CloseIdle == "true" {
return "i2cp.closeOnIdle=" + f.CloseIdle + "i2cp.closeIdleTime=" + f.CloseIdleTime
}
return ""
}
func (f *I2PConfig) DoZero() string {
r := ""
if f.InAllowZeroHop == "true" {
r += " inbound.allowZeroHop=" + f.InAllowZeroHop + " "
}
if f.OutAllowZeroHop == "true" {
r += " outbound.allowZeroHop= " + f.OutAllowZeroHop + " "
}
if f.FastRecieve == "true" {
r += " " + f.FastRecieve + " "
}
return r
}
func (f *I2PConfig) Print() []string {
lsk, lspk, lspsk := f.Leasesetsettings()
return []string{
//f.targetForPort443(),
"inbound.length=" + f.InLength,
"outbound.length=" + f.OutLength,
"inbound.lengthVariance=" + f.InVariance,
"outbound.lengthVariance=" + f.OutVariance,
"inbound.backupQuantity=" + f.InBackupQuantity,
"outbound.backupQuantity=" + f.OutBackupQuantity,
"inbound.quantity=" + f.InQuantity,
"outbound.quantity=" + f.OutQuantity,
f.DoZero(),
//"i2cp.fastRecieve=" + f.FastRecieve,
"i2cp.gzip=" + f.UseCompression,
f.Reduce(),
f.Close(),
f.Reliability(),
f.EncryptLease(),
lsk, lspk, lspsk,
f.Accesslisttype(),
f.Accesslist(),
}
}
func (f *I2PConfig) Accesslisttype() string {
if f.AccessListType == "whitelist" {
return "i2cp.enableAccessList=true"
} else if f.AccessListType == "blacklist" {
return "i2cp.enableBlackList=true"
} else if f.AccessListType == "none" {
return ""
}
return ""
}
func (f *I2PConfig) Accesslist() string {
if f.AccessListType != "" && len(f.AccessList) > 0 {
r := ""
for _, s := range f.AccessList {
r += s + ","
}
return "i2cp.accessList=" + strings.TrimSuffix(r, ",")
}
return ""
}
func NewConfig(opts ...func(*I2PConfig) error) (*I2PConfig, error) {
var config I2PConfig
config.SamHost = "127.0.0.1"
config.SamPort = "7656"
config.SamMin = "3.0"
config.SamMax = "3.2"
config.TunName = ""
config.TunType = "server"
config.Style = "STREAM"
config.InLength = "3"
config.OutLength = "3"
config.InQuantity = "2"
config.OutQuantity = "2"
config.InVariance = "1"
config.OutVariance = "1"
config.InBackupQuantity = "3"
config.OutBackupQuantity = "3"
config.InAllowZeroHop = "false"
config.OutAllowZeroHop = "false"
config.EncryptLeaseSet = "false"
config.LeaseSetKey = ""
config.LeaseSetPrivateKey = ""
config.LeaseSetPrivateSigningKey = ""
config.FastRecieve = "false"
config.UseCompression = "true"
config.ReduceIdle = "false"
config.ReduceIdleTime = "15"
config.ReduceIdleQuantity = "4"
config.CloseIdle = "false"
config.CloseIdleTime = "300000"
config.MessageReliability = "none"
for _, o := range opts {
if err := o(&config); err != nil {
return nil, err
}
}
return &config, nil
}
// options map
type Options map[string]string
@@ -38,7 +324,7 @@ func (cfg *Config) StreamSession() (session *StreamSession, err error) {
s, err = NewSAM(cfg.Addr)
if err == nil {
// ensure keys exist
var keys I2PKeys
var keys i2pkeys.I2PKeys
keys, err = s.EnsureKeyfile(cfg.Keyfile)
if err == nil {
// create session
@@ -55,7 +341,7 @@ func (cfg *Config) DatagramSession() (session *DatagramSession, err error) {
s, err = NewSAM(cfg.Addr)
if err == nil {
// ensure keys exist
var keys I2PKeys
var keys i2pkeys.I2PKeys
keys, err = s.EnsureKeyfile(cfg.Keyfile)
if err == nil {
// determine udp port

View File

@@ -6,10 +6,8 @@ import (
"net"
"strconv"
"time"
)
import (
. "github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/sam3/i2pkeys"
)
// The DatagramSession implements net.PacketConn. It works almost like ordinary
@@ -17,17 +15,18 @@ import (
// also end-to-end encrypted, signed and includes replay-protection. And they
// are also built to be surveillance-resistant (yey!).
type DatagramSession struct {
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam bridge
udpconn *net.UDPConn // used to deliver datagrams
keys I2PKeys // i2p destination keys
rUDPAddr *net.UDPAddr // the SAM bridge UDP-port
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam bridge
udpconn *net.UDPConn // used to deliver datagrams
keys i2pkeys.I2PKeys // i2p destination keys
rUDPAddr *net.UDPAddr // the SAM bridge UDP-port
remoteAddr *i2pkeys.I2PAddr // optional remote I2P address
}
// Creates a new datagram session. udpPort is the UDP port SAM is listening on,
// and if you set it to zero, it will use SAMs standard UDP port.
func (s *SAM) NewDatagramSession(id string, keys I2PKeys, options []string, udpPort int) (*DatagramSession, error) {
func (s *SAM) NewDatagramSession(id string, keys i2pkeys.I2PKeys, options []string, udpPort int) (*DatagramSession, error) {
if udpPort > 65335 || udpPort < 0 {
return nil, errors.New("udpPort needs to be in the intervall 0-65335")
}
@@ -61,13 +60,44 @@ func (s *SAM) NewDatagramSession(id string, keys I2PKeys, options []string, udpP
if err != nil {
return nil, err
}
return &DatagramSession{s.Config.I2PConfig.Sam(), id, conn, udpconn, keys, rUDPAddr}, nil
return &DatagramSession{s.address, id, conn, udpconn, keys, rUDPAddr, nil}, nil
}
func (s *DatagramSession) B32() string {
return s.keys.Addr().Base32()
}
func (s *DatagramSession) Dial(net string, addr string) (*DatagramSession, error) {
netaddr, err := s.Lookup(addr)
if err != nil {
return nil, err
}
return s.DialI2PRemote(net, netaddr)
}
func (s *DatagramSession) DialRemote(net, addr string) (net.PacketConn, error) {
netaddr, err := s.Lookup(addr)
if err != nil {
return nil, err
}
return s.DialI2PRemote(net, netaddr)
}
func (s *DatagramSession) DialI2PRemote(net string, addr net.Addr) (*DatagramSession, error) {
switch addr.(type) {
case *i2pkeys.I2PAddr:
s.remoteAddr = addr.(*i2pkeys.I2PAddr)
case i2pkeys.I2PAddr:
i2paddr := addr.(i2pkeys.I2PAddr)
s.remoteAddr = &i2paddr
}
return s, nil
}
func (s *DatagramSession) RemoteAddr() net.Addr {
return s.remoteAddr
}
// Reads one datagram sent to the destination of the DatagramSession. Returns
// the number of bytes read, from what address it was sent, or an error.
// implements net.PacketConn
@@ -80,7 +110,7 @@ func (s *DatagramSession) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
var saddr *net.UDPAddr
n, saddr, err = s.udpconn.ReadFromUDP(buf)
if err != nil {
return 0, I2PAddr(""), err
return 0, i2pkeys.I2PAddr(""), err
}
if bytes.Equal(saddr.IP, s.rUDPAddr.IP) {
continue
@@ -89,11 +119,11 @@ func (s *DatagramSession) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
}
i := bytes.IndexByte(buf, byte('\n'))
if i > 4096 || i > n {
return 0, I2PAddr(""), errors.New("Could not parse incomming message remote address.")
return 0, i2pkeys.I2PAddr(""), errors.New("Could not parse incomming message remote address.")
}
raddr, err := NewI2PAddrFromString(string(buf[:i]))
raddr, err := i2pkeys.NewI2PAddrFromString(string(buf[:i]))
if err != nil {
return 0, I2PAddr(""), errors.New("Could not parse incomming message remote address: " + err.Error())
return 0, i2pkeys.I2PAddr(""), errors.New("Could not parse incomming message remote address: " + err.Error())
}
// shift out the incomming address to contain only the data received
if (n - i + 1) > len(b) {
@@ -105,16 +135,29 @@ func (s *DatagramSession) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
}
}
func (s *DatagramSession) Accept() (net.Conn, error) {
return s, nil
}
func (s *DatagramSession) Read(b []byte) (n int, err error) {
rint, _, rerr := s.ReadFrom(b)
return rint, rerr
}
// Sends one signed datagram to the destination specified. At the time of
// writing, maximum size is 31 kilobyte, but this may change in the future.
// Implements net.PacketConn.
func (s *DatagramSession) WriteTo(b []byte, addr net.Addr) (n int, err error) {
header := []byte("3.0 " + s.id + " " + addr.String() + "\n")
header := []byte("3.1 " + s.id + " " + addr.String() + "\n")
msg := append(header, b...)
n, err = s.udpconn.WriteToUDP(msg, s.rUDPAddr)
return n, err
}
func (s *DatagramSession) Write(b []byte) (int, error) {
return s.WriteTo(b, s.remoteAddr)
}
// Closes the DatagramSession. Implements net.PacketConn
func (s *DatagramSession) Close() error {
err := s.conn.Close()
@@ -126,7 +169,7 @@ func (s *DatagramSession) Close() error {
}
// Returns the I2P destination of the DatagramSession.
func (s *DatagramSession) LocalI2PAddr() I2PAddr {
func (s *DatagramSession) LocalI2PAddr() i2pkeys.I2PAddr {
return s.keys.Addr()
}
@@ -135,6 +178,10 @@ func (s *DatagramSession) LocalAddr() net.Addr {
return s.LocalI2PAddr()
}
func (s *DatagramSession) Addr() net.Addr {
return s.LocalI2PAddr()
}
func (s *DatagramSession) Lookup(name string) (a net.Addr, err error) {
var sam *SAM
sam, err = NewSAM(s.samAddr)
@@ -161,3 +208,7 @@ func (s *DatagramSession) SetReadDeadline(t time.Time) error {
func (s *DatagramSession) SetWriteDeadline(t time.Time) error {
return s.udpconn.SetWriteDeadline(t)
}
func (s *DatagramSession) SetWriteBuffer(bytes int) error {
return s.udpconn.SetWriteBuffer(bytes)
}

View File

@@ -4,6 +4,7 @@ package sam3
import (
"fmt"
"log"
"testing"
"time"
)
@@ -124,6 +125,58 @@ func ExampleDatagramSession() {
fmt.Println(err.Error())
return
}
log.Println("Got message: '" + string(buf[:n]) + "'")
fmt.Println("Got message: " + string(buf[:n]))
return
// Output:
//Got message: Hello myself!
}
func ExampleMiniDatagramSession() {
// Creates a new DatagramSession, which behaves just like a net.PacketConn.
const samBridge = "127.0.0.1:7656"
sam, err := NewSAM(samBridge)
if err != nil {
fmt.Println(err.Error())
return
}
keys, err := sam.NewKeys()
if err != nil {
fmt.Println(err.Error())
return
}
myself := keys.Addr()
// See the example Option_* variables.
dg, err := sam.NewDatagramSession("MINIDGTUN", keys, Options_Small, 0)
if err != nil {
fmt.Println(err.Error())
return
}
someone, err := sam.Lookup("zzz.i2p")
if err != nil {
fmt.Println(err.Error())
return
}
err = dg.SetWriteBuffer(14 * 1024)
if err != nil {
fmt.Println(err.Error())
return
}
dg.WriteTo([]byte("Hello stranger!"), someone)
dg.WriteTo([]byte("Hello myself!"), myself)
buf := make([]byte, 31*1024)
n, _, err := dg.ReadFrom(buf)
if err != nil {
fmt.Println(err.Error())
return
}
log.Println("Got message: '" + string(buf[:n]) + "'")
fmt.Println("Got message: " + string(buf[:n]))
return

53
debian/changelog vendored
View File

@@ -1,3 +1,56 @@
golang-github-eyedeekay-sam3 (0.3.2.32) unreleased; urgency=medium
[ idk ]
* Include "Convenience Functions" in /helper
-- idk <hankhill19580@gmail.com> Sun, 22 Nov 2020 12:49:13 -0500
golang-github-eyedeekay-sam3 (0.3.2.31) unreleased; urgency=medium
[ idk ]
* Incorporate address fix from qiwenmin
* Shortcut for saving keys to text file
-- idk <hankhill19580@gmail.com> Sun, 18 Oct 2020 1:01:03 -0500
golang-github-eyedeekay-sam3 (0.3.2.3) unreleased; urgency=medium
[ idk ]
* Purge ramp, re-release
-- idk <hankhill19580@gmail.com> Mon, 22 Jun 2020 17:55:03 -0500
golang-github-eyedeekay-sam3 (0.3.2.2) bionic; urgency=medium
[ idk ]
* Purge ramp
-- idk <hankhill19580@gmail.com> Sat, 7 Dec 2019 22:05:34 -0500
golang-github-eyedeekay-sam3 (0.3.2.1) bionic; urgency=medium
[ idk ]
* Add support for 44-character destination hashes.
-- idk <hankhill19580@gmail.com> Sat, 7 Dec 2019 17:30:30 -0500
golang-github-eyedeekay-sam3 (0.3.2.01) bionic; urgency=medium
[ idk ]
* completely remove the old i2pkeys version and replace it with the new one.
-- idk <hankhill19580@gmail.com> Sat, 25 May 2019 14:38:11 -0500
golang-github-eyedeekay-sam3 (0.3.2.0) bionic; urgency=medium
[ idk ]
* Bug fixes, create stable branch
* Move i2pkeys
-- idk <hankhill19580@gmail.com> Sat, 18 May 2019 18:32:51 -0500
golang-github-eyedeekay-sam3 (0.0~git20190223.af5a3f3) bionic; urgency=medium
[ idk ]

6
debian/control vendored
View File

@@ -5,7 +5,8 @@ Maintainer: Debian Go Packaging Team <team+pkg-go@tracker.debian.org>
Uploaders: idk <hankhill19580@gmail.com>
Build-Depends: debhelper (>= 11),
dh-golang,
golang-any
git,
golang-any | golang-go,
Standards-Version: 4.2.1
Homepage: https://github.com/eyedeekay/sam3
Vcs-Browser: https://github.com/eyedeekay/sam3
@@ -15,7 +16,8 @@ Testsuite: autopkgtest-pkg-go
Package: golang-github-eyedeekay-sam3-dev
Architecture: all
Depends: ${misc:Depends}, (i2p | i2pd)
Depends: ${misc:Depends},
i2p | i2pd
Description: Go library for the I2P SAMv3.0 bridge,
used to build anonymous/pseudonymous end-to-end encrypted sockets.
README go library for the I2P SAMv3.0

1
debian/files vendored Normal file
View File

@@ -0,0 +1 @@
golang-github-eyedeekay-sam3_0.3.2.32_source.buildinfo devel optional

369
emit-options.go Normal file
View File

@@ -0,0 +1,369 @@
package sam3
import (
"fmt"
"strconv"
"strings"
)
//Option is a SAMEmit Option
type Option func(*SAMEmit) error
//SetType sets the type of the forwarder server
func SetType(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if s == "STREAM" {
c.Style = s
return nil
} else if s == "DATAGRAM" {
c.Style = s
return nil
} else if s == "RAW" {
c.Style = s
return nil
}
return fmt.Errorf("Invalid session STYLE=%s, must be STREAM, DATAGRAM, or RAW", s)
}
}
// SetSAMAddress sets the SAM address all-at-once
func SetSAMAddress(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
sp := strings.Split(s, ":")
if len(sp) > 2 {
return fmt.Errorf("Invalid address string: %s", sp)
}
if len(sp) == 2 {
c.I2PConfig.SamPort = sp[1]
}
c.I2PConfig.SamHost = sp[0]
return nil
}
}
//SetSAMHost sets the host of the SAMEmit's SAM bridge
func SetSAMHost(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.SamHost = s
return nil
}
}
//SetSAMPort sets the port of the SAMEmit's SAM bridge using a string
func SetSAMPort(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
port, err := strconv.Atoi(s)
if err != nil {
return fmt.Errorf("Invalid SAM Port %s; non-number", s)
}
if port < 65536 && port > -1 {
c.I2PConfig.SamPort = s
return nil
}
return fmt.Errorf("Invalid port")
}
}
//SetName sets the host of the SAMEmit's SAM bridge
func SetName(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.TunName = s
return nil
}
}
//SetInLength sets the number of hops inbound
func SetInLength(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u < 7 && u >= 0 {
c.I2PConfig.InLength = strconv.Itoa(u)
return nil
}
return fmt.Errorf("Invalid inbound tunnel length")
}
}
//SetOutLength sets the number of hops outbound
func SetOutLength(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u < 7 && u >= 0 {
c.I2PConfig.OutLength = strconv.Itoa(u)
return nil
}
return fmt.Errorf("Invalid outbound tunnel length")
}
}
//SetInVariance sets the variance of a number of hops inbound
func SetInVariance(i int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if i < 7 && i > -7 {
c.I2PConfig.InVariance = strconv.Itoa(i)
return nil
}
return fmt.Errorf("Invalid inbound tunnel length")
}
}
//SetOutVariance sets the variance of a number of hops outbound
func SetOutVariance(i int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if i < 7 && i > -7 {
c.I2PConfig.OutVariance = strconv.Itoa(i)
return nil
}
return fmt.Errorf("Invalid outbound tunnel variance")
}
}
//SetInQuantity sets the inbound tunnel quantity
func SetInQuantity(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u <= 16 && u > 0 {
c.I2PConfig.InQuantity = strconv.Itoa(u)
return nil
}
return fmt.Errorf("Invalid inbound tunnel quantity")
}
}
//SetOutQuantity sets the outbound tunnel quantity
func SetOutQuantity(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u <= 16 && u > 0 {
c.I2PConfig.OutQuantity = strconv.Itoa(u)
return nil
}
return fmt.Errorf("Invalid outbound tunnel quantity")
}
}
//SetInBackups sets the inbound tunnel backups
func SetInBackups(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u < 6 && u >= 0 {
c.I2PConfig.InBackupQuantity = strconv.Itoa(u)
return nil
}
return fmt.Errorf("Invalid inbound tunnel backup quantity")
}
}
//SetOutBackups sets the inbound tunnel backups
func SetOutBackups(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u < 6 && u >= 0 {
c.I2PConfig.OutBackupQuantity = strconv.Itoa(u)
return nil
}
return fmt.Errorf("Invalid outbound tunnel backup quantity")
}
}
//SetEncrypt tells the router to use an encrypted leaseset
func SetEncrypt(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
c.I2PConfig.EncryptLeaseSet = "true"
return nil
}
c.I2PConfig.EncryptLeaseSet = "false"
return nil
}
}
//SetLeaseSetKey sets the host of the SAMEmit's SAM bridge
func SetLeaseSetKey(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.LeaseSetKey = s
return nil
}
}
//SetLeaseSetPrivateKey sets the host of the SAMEmit's SAM bridge
func SetLeaseSetPrivateKey(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.LeaseSetPrivateKey = s
return nil
}
}
//SetLeaseSetPrivateSigningKey sets the host of the SAMEmit's SAM bridge
func SetLeaseSetPrivateSigningKey(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.LeaseSetPrivateSigningKey = s
return nil
}
}
//SetMessageReliability sets the host of the SAMEmit's SAM bridge
func SetMessageReliability(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.MessageReliability = s
return nil
}
}
//SetAllowZeroIn tells the tunnel to accept zero-hop peers
func SetAllowZeroIn(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
c.I2PConfig.InAllowZeroHop = "true"
return nil
}
c.I2PConfig.InAllowZeroHop = "false"
return nil
}
}
//SetAllowZeroOut tells the tunnel to accept zero-hop peers
func SetAllowZeroOut(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
c.I2PConfig.OutAllowZeroHop = "true"
return nil
}
c.I2PConfig.OutAllowZeroHop = "false"
return nil
}
}
//SetCompress tells clients to use compression
func SetCompress(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
c.I2PConfig.UseCompression = "true"
return nil
}
c.I2PConfig.UseCompression = "false"
return nil
}
}
//SetFastRecieve tells clients to use compression
func SetFastRecieve(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
c.I2PConfig.FastRecieve = "true"
return nil
}
c.I2PConfig.FastRecieve = "false"
return nil
}
}
//SetReduceIdle tells the connection to reduce it's tunnels during extended idle time.
func SetReduceIdle(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
c.I2PConfig.ReduceIdle = "true"
return nil
}
c.I2PConfig.ReduceIdle = "false"
return nil
}
}
//SetReduceIdleTime sets the time to wait before reducing tunnels to idle levels
func SetReduceIdleTime(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.ReduceIdleTime = "300000"
if u >= 6 {
c.I2PConfig.ReduceIdleTime = strconv.Itoa((u * 60) * 1000)
return nil
}
return fmt.Errorf("Invalid reduce idle timeout(Measured in minutes) %v", u)
}
}
//SetReduceIdleTimeMs sets the time to wait before reducing tunnels to idle levels in milliseconds
func SetReduceIdleTimeMs(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.ReduceIdleTime = "300000"
if u >= 300000 {
c.I2PConfig.ReduceIdleTime = strconv.Itoa(u)
return nil
}
return fmt.Errorf("Invalid reduce idle timeout(Measured in milliseconds) %v", u)
}
}
//SetReduceIdleQuantity sets minimum number of tunnels to reduce to during idle time
func SetReduceIdleQuantity(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if u < 5 {
c.I2PConfig.ReduceIdleQuantity = strconv.Itoa(u)
return nil
}
return fmt.Errorf("Invalid reduce tunnel quantity")
}
}
//SetCloseIdle tells the connection to close it's tunnels during extended idle time.
func SetCloseIdle(b bool) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if b {
c.I2PConfig.CloseIdle = "true"
return nil
}
c.I2PConfig.CloseIdle = "false"
return nil
}
}
//SetCloseIdleTime sets the time to wait before closing tunnels to idle levels
func SetCloseIdleTime(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.CloseIdleTime = "300000"
if u >= 6 {
c.I2PConfig.CloseIdleTime = strconv.Itoa((u * 60) * 1000)
return nil
}
return fmt.Errorf("Invalid close idle timeout(Measured in minutes) %v", u)
}
}
//SetCloseIdleTimeMs sets the time to wait before closing tunnels to idle levels in milliseconds
func SetCloseIdleTimeMs(u int) func(*SAMEmit) error {
return func(c *SAMEmit) error {
c.I2PConfig.CloseIdleTime = "300000"
if u >= 300000 {
c.I2PConfig.CloseIdleTime = strconv.Itoa(u)
return nil
}
return fmt.Errorf("Invalid close idle timeout(Measured in milliseconds) %v", u)
}
}
//SetAccessListType tells the system to treat the AccessList as a whitelist
func SetAccessListType(s string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if s == "whitelist" {
c.I2PConfig.AccessListType = "whitelist"
return nil
} else if s == "blacklist" {
c.I2PConfig.AccessListType = "blacklist"
return nil
} else if s == "none" {
c.I2PConfig.AccessListType = ""
return nil
} else if s == "" {
c.I2PConfig.AccessListType = ""
return nil
}
return fmt.Errorf("Invalid Access list type(whitelist, blacklist, none)")
}
}
//SetAccessList tells the system to treat the AccessList as a whitelist
func SetAccessList(s []string) func(*SAMEmit) error {
return func(c *SAMEmit) error {
if len(s) > 0 {
for _, a := range s {
c.I2PConfig.AccessList = append(c.I2PConfig.AccessList, a)
}
return nil
}
return nil
}
}

98
emit.go Normal file
View File

@@ -0,0 +1,98 @@
package sam3
import (
"fmt"
"log"
)
type SAMEmit struct {
I2PConfig
}
func (e *SAMEmit) OptStr() string {
optStr := ""
for _, opt := range e.I2PConfig.Print() {
optStr += opt + " "
}
return optStr
}
func (e *SAMEmit) Hello() string {
return fmt.Sprintf("HELLO VERSION MIN=%s MAX=%s \n", e.I2PConfig.MinSAM(), e.I2PConfig.MaxSAM())
}
func (e *SAMEmit) HelloBytes() []byte {
return []byte(e.Hello())
}
func (e *SAMEmit) GenerateDestination() string {
return fmt.Sprintf("DEST GENERATE %s \n", e.I2PConfig.SignatureType())
}
func (e *SAMEmit) GenerateDestinationBytes() []byte {
return []byte(e.GenerateDestination())
}
func (e *SAMEmit) Lookup(name string) string {
return fmt.Sprintf("NAMING LOOKUP NAME=%s \n", name)
}
func (e *SAMEmit) LookupBytes(name string) []byte {
return []byte(e.Lookup(name))
}
func (e *SAMEmit) Create() string {
return fmt.Sprintf(
// //1 2 3 4 5 6 7
"SESSION CREATE %s%s%s%s%s%s%s \n",
e.I2PConfig.SessionStyle(), //1
e.I2PConfig.FromPort(), //2
e.I2PConfig.ToPort(), //3
e.I2PConfig.ID(), //4
e.I2PConfig.DestinationKey(), // 5
e.I2PConfig.SignatureType(), // 6
e.OptStr(), // 7
)
}
func (e *SAMEmit) CreateBytes() []byte {
log.Println("sam command: " + e.Create())
return []byte(e.Create())
}
func (e *SAMEmit) Connect(dest string) string {
return fmt.Sprintf(
"STREAM CONNECT ID=%s %s %s DESTINATION=%s \n",
e.I2PConfig.ID(),
e.I2PConfig.FromPort(),
e.I2PConfig.ToPort(),
dest,
)
}
func (e *SAMEmit) ConnectBytes(dest string) []byte {
return []byte(e.Connect(dest))
}
func (e *SAMEmit) Accept() string {
return fmt.Sprintf(
"STREAM ACCEPT ID=%s %s %s",
e.I2PConfig.ID(),
e.I2PConfig.FromPort(),
e.I2PConfig.ToPort(),
)
}
func (e *SAMEmit) AcceptBytes() []byte {
return []byte(e.Accept())
}
func NewEmit(opts ...func(*SAMEmit) error) (*SAMEmit, error) {
var emit SAMEmit
for _, o := range opts {
if err := o(&emit); err != nil {
return nil, err
}
}
return &emit, nil
}

12
go.mod Normal file
View File

@@ -0,0 +1,12 @@
module github.com/eyedeekay/sam3
go 1.12
require (
github.com/eyedeekay/goSam v0.32.31-0.20210122211817-f97683379f23
github.com/google/renameio v1.0.0 // indirect
github.com/riobard/go-x25519 v0.0.0-20190716001027-10cc4d8d0b33
github.com/rogpeppe/go-internal v1.6.2 // indirect
golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b // indirect
honnef.co/go/tools v0.0.1-2020.1.6 // indirect
)

88
go.sum Normal file
View File

@@ -0,0 +1,88 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/agl/ed25519 v0.0.0-20200225211852-fd4d107ace12 h1:iPf1jQ8yKTms6k6L5vYSE7RZJpjEe5vLTOmzRZdpnKc=
github.com/cryptix/go v1.3.1 h1:I9opbROgEpldI0PwkMku0UY2DLFYgelZd9u0uaxmMgY=
github.com/cryptix/go v1.3.1/go.mod h1:mFQotm9rTzptzvNjJM+1vSIDa/rVOVqMu0889GIXg70=
github.com/cryptix/goSam v0.1.0 h1:lKXtrTv3Kd6+eIuNtcq3zPShJEVRqw+lQwmh49HmC7k=
github.com/cryptix/goSam v0.1.0/go.mod h1:7ewkjhXT8V5RG07pvWUOHHtMahvGbeKlEv8ukUyRiTA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eyedeekay/goSam v0.32.30 h1:mMlZNE2oISdjjjpgfN17W56tn9F8rD/Jc2tsjTDDFYg=
github.com/eyedeekay/goSam v0.32.30/go.mod h1:UgJnih/LpotwKriwVPOEa6yPDM2NDdVrKfLtS5DOLPE=
github.com/eyedeekay/goSam v0.32.31-0.20210122211024-dddd8ea916d6 h1:seMFdfTWvmAsyj9jYPZATBJiJu5gHPsvJJk4Ruo2npQ=
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/go.mod h1:UgJnih/LpotwKriwVPOEa6yPDM2NDdVrKfLtS5DOLPE=
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/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
github.com/getlantern/go-socks5 v0.0.0-20171114193258-79d4dd3e2db5/go.mod h1:kGHRXch95rnGLHjER/GhhFiHvfnqNz7KqWD9kGfATHY=
github.com/getlantern/golog v0.0.0-20201105130739-9586b8bde3a9/go.mod h1:ZyIjgH/1wTCl+B+7yH1DqrWp6MPJqESmwmEQ89ZfhvA=
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o=
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA=
github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd/go.mod h1:wKdY0ikOgzrWSeB9UyBVKPRhjXQ+vTb+BPeJuypUuNE=
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
github.com/go-kit/kit v0.6.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-stack/stack v1.7.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/renameio v1.0.0 h1:xhp2CnJmgQmpJU4RY8chagahUq5mbPPAbiSQstKpVMA=
github.com/google/renameio v1.0.0/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/miolini/datacounter v0.0.0-20171104152933-fd4e42a1d5e0 h1:clkDYGefEWUCwyCrwYn900sOaVGDpinPJgD0W6ebEjs=
github.com/miolini/datacounter v0.0.0-20171104152933-fd4e42a1d5e0/go.mod h1:P6fDJzlxN+cWYR09KbE9/ta+Y6JofX9tAUhJpWkWPaM=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/riobard/go-x25519 v0.0.0-20190716001027-10cc4d8d0b33 h1:dyyWDK0yzlZ8ay89Oe5ZIRtscacUjFyPUFGChrgMXRg=
github.com/riobard/go-x25519 v0.0.0-20190716001027-10cc4d8d0b33/go.mod h1:BjmVxzAnkLeoEbqHEerI4eSw6ua+RaIB0S4jMV21RAs=
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0=
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef h1:RHORRhs540cYZYrzgU2CPUyykkwZM78hGdzocOo9P8A=
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b h1:Lq5JUTFhiybGVf28jB6QRpqd13/JPOaCnET17PVzYJE=
golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
honnef.co/go/tools v0.0.1-2020.1.6 h1:W18jzjh8mfPez+AwGLxmOImucz/IFjpNlrKVnaj2YVc=
honnef.co/go/tools v0.0.1-2020.1.6/go.mod h1:pyyisuGw24ruLjrr1ddx39WE0y9OooInRzEYLhQB2YY=

117
helper/helper.go Normal file
View File

@@ -0,0 +1,117 @@
package sam
import (
"io/ioutil"
"log"
"net"
"os"
"github.com/eyedeekay/sam3"
"github.com/eyedeekay/sam3/i2pkeys"
)
func NetListener(name, samaddr, keyspath string) (net.Listener, error) {
return I2PListener(name, samaddr, keyspath)
}
// I2PListener is a convenience function which takes a SAM tunnel name, a SAM address and a filename.
// If the file contains I2P keys, it will create a service using that address. If the file does not
// exist, keys will be generated and stored in that file.
func I2PListener(name, samaddr, keyspath string) (*sam3.StreamListener, error) {
log.Printf("Starting and registering I2P service, please wait a couple of minutes...")
listener, err := I2PStreamSession(name, samaddr, keyspath)
if keyspath != "" {
err = ioutil.WriteFile(keyspath+".i2p.public.txt", []byte(listener.Keys().Addr().Base32()), 0644)
if err != nil {
log.Fatalf("error storing I2P base32 address in adjacent text file, %s", err)
}
}
log.Printf("Listening on: %s", listener.Addr().Base32())
return listener.Listen()
}
// I2PStreamSession is a convenience function which returns a sam3.StreamSession instead
// of a sam3.StreamListener. It also takes care of setting a persisitent key on behalf
// of the user.
func I2PStreamSession(name, samaddr, keyspath string) (*sam3.StreamSession, error) {
log.Printf("Starting and registering I2P session...")
sam, err := sam3.NewSAM(samaddr)
if err != nil {
log.Fatalf("error connecting to SAM to %s: %s", samaddr, err)
}
keys, err := GenerateOrLoadKeys(keyspath, sam)
if err != nil {
return nil, err
}
stream, err := sam.NewStreamSession(name, *keys, sam3.Options_Medium)
return stream, err
}
// I2PDataGramsession is a convenience function which returns a sam3.DatagramSession.
// It also takes care of setting a persisitent key on behalf of the user.
func I2PDatagramSession(name, samaddr, keyspath string) (*sam3.DatagramSession, error) {
log.Printf("Starting and registering I2P session...")
sam, err := sam3.NewSAM(samaddr)
if err != nil {
log.Fatalf("error connecting to SAM to %s: %s", samaddr, err)
}
keys, err := GenerateOrLoadKeys(keyspath, sam)
if err != nil {
return nil, err
}
gram, err := sam.NewDatagramSession(name, *keys, sam3.Options_Medium, 0)
return gram, err
}
// I2PPrimarySession is a convenience function which returns a sam3.PrimarySession.
// It also takes care of setting a persisitent key on behalf of the user.
func I2PPrimarySession(name, samaddr, keyspath string) (*sam3.PrimarySession, error) {
log.Printf("Starting and registering I2P session...")
sam, err := sam3.NewSAM(samaddr)
if err != nil {
log.Fatalf("error connecting to SAM to %s: %s", samaddr, err)
}
keys, err := GenerateOrLoadKeys(keyspath, sam)
if err != nil {
return nil, err
}
gram, err := sam.NewPrimarySession(name, *keys, sam3.Options_Medium)
return gram, err
}
func GenerateOrLoadKeys(keyspath string, sam *sam3.SAM) (keys *i2pkeys.I2PKeys, err error) {
if sam == nil {
sam, err = sam3.NewSAM("127.0.0.1:7657")
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
}
func GenerateKeys(keyspath string) (keys *i2pkeys.I2PKeys, err error) {
return GenerateOrLoadKeys(keyspath, nil)
}

325
i2pkeys/I2PAddr.go Normal file
View File

@@ -0,0 +1,325 @@
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")
)
// 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()
}
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
View 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())
}

377
primary.go Normal file
View File

@@ -0,0 +1,377 @@
package sam3
import (
"errors"
"fmt"
"math/rand"
"net"
"strconv"
"strings"
"time"
"github.com/eyedeekay/sam3/i2pkeys"
)
const (
session_ADDOK = "SESSION STATUS RESULT=OK"
)
func randport() string {
s := rand.NewSource(time.Now().UnixNano())
r := rand.New(s)
p := r.Intn(55534) + 10000
return strconv.Itoa(p)
}
// Represents a primary session.
type PrimarySession struct {
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam
keys i2pkeys.I2PKeys // i2p destination keys
Timeout time.Duration
Deadline time.Time
sigType string
Config SAMEmit
stsess map[string]*StreamSession
dgsess map[string]*DatagramSession
// from string
// to string
}
func (ss *PrimarySession) From() string {
return "0"
}
func (ss *PrimarySession) To() string {
return "0"
}
func (ss *PrimarySession) SignatureType() string {
return ss.sigType
}
// Returns the local tunnel name of the I2P tunnel used for the stream session
func (ss *PrimarySession) ID() string {
return ss.id
}
func (ss *PrimarySession) Close() error {
return ss.conn.Close()
}
// Returns the I2P destination (the address) of the stream session
func (ss *PrimarySession) Addr() i2pkeys.I2PAddr {
return ss.keys.Addr()
}
func (ss *PrimarySession) LocalAddr() net.Addr {
aa := ss.keys.Addr()
return &aa
}
// Returns the keys associated with the stream session
func (ss *PrimarySession) Keys() i2pkeys.I2PKeys {
return ss.keys
}
func (sam *PrimarySession) Dial(network, addr string) (net.Conn, error) {
if network == "udp" || network == "udp4" || network == "udp6" {
return sam.DialUDPI2P(network, network+addr[0:4], addr)
}
if network == "tcp" || network == "tcp4" || network == "tcp6" {
return sam.DialTCPI2P(network, network+addr[0:4], addr)
}
return nil, fmt.Errorf("Error: Must specify a valid network type")
}
// DialTCP implements x/dialer
func (sam *PrimarySession) DialTCP(network string, laddr, raddr net.Addr) (net.Conn, error) {
_, ok := sam.stsess[network+raddr.String()[0:4]]
if !ok {
stsess, err := sam.NewUniqueStreamSubSession(network + raddr.String()[0:4])
if err != nil {
return nil, err
}
sam.stsess[network+raddr.String()[0:4]] = stsess
}
return sam.stsess[network+raddr.String()[0:4]].Dial(network, raddr.String())
}
func (sam *PrimarySession) DialTCPI2P(network string, laddr, raddr string) (net.Conn, error) {
_, ok := sam.stsess[network+raddr[0:4]]
if !ok {
stsess, err := sam.NewUniqueStreamSubSession(network + laddr)
if err != nil {
return nil, err
}
sam.stsess[network+raddr[0:4]] = stsess
}
return sam.stsess[network+raddr[0:4]].Dial(network, raddr)
}
// DialUDP implements x/dialer
func (sam *PrimarySession) DialUDP(network string, laddr, raddr net.Addr) (net.PacketConn, error) {
_, ok := sam.dgsess[network+raddr.String()[0:4]]
if !ok {
dgsess, err := sam.NewDatagramSubSession(network+raddr.String()[0:4], 0)
if err != nil {
return nil, err
}
sam.dgsess[network+raddr.String()[0:4]] = dgsess
}
return sam.dgsess[network+raddr.String()[0:4]].Dial(network, raddr.String())
}
func (sam *PrimarySession) DialUDPI2P(network, laddr, raddr string) (*DatagramSession, error) {
_, ok := sam.dgsess[network+raddr[0:4]]
if !ok {
dgsess, err := sam.NewDatagramSubSession(network+laddr, 0)
if err != nil {
return nil, err
}
sam.dgsess[network+raddr[0:4]] = dgsess
}
return sam.dgsess[network+raddr[0:4]].Dial(network, raddr)
}
func (s *PrimarySession) Lookup(name string) (a net.Addr, err error) {
var sam *SAM
if len(strings.Split(name, ":")) <= 1 {
name += ":0"
}
sam, err = NewSAM(s.samAddr)
if err == nil {
defer sam.Close()
a, err = sam.Lookup(name)
}
return
}
func (sam *PrimarySession) Resolve(network, addr string) (net.Addr, error) {
return sam.Lookup(addr)
}
func (sam *PrimarySession) ResolveTCPAddr(network, dest string) (net.Addr, error) {
return sam.Lookup(dest)
}
func (sam *PrimarySession) ResolveUDPAddr(network, dest string) (net.Addr, error) {
return sam.Lookup(dest)
}
// Creates a new PrimarySession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewPrimarySession(id string, keys i2pkeys.I2PKeys, options []string) (*PrimarySession, error) {
conn, err := sam.newGenericSession("PRIMARY", id, keys, options, []string{})
if err != nil {
return nil, err
}
ssesss := make(map[string]*StreamSession)
dsesss := make(map[string]*DatagramSession)
return &PrimarySession{sam.Config.I2PConfig.Sam(), id, conn, keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, sam.Config, ssesss, dsesss}, nil
}
// Creates a new PrimarySession with the I2CP- and PRIMARYinglib options as
// 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) {
conn, err := sam.newGenericSessionWithSignature("PRIMARY", id, keys, sigType, options, []string{})
if err != nil {
return nil, err
}
ssesss := make(map[string]*StreamSession)
dsesss := make(map[string]*DatagramSession)
return &PrimarySession{sam.Config.I2PConfig.Sam(), id, conn, keys, time.Duration(600 * time.Second), time.Now(), sigType, sam.Config, ssesss, dsesss}, nil
}
// Creates a new session with the style of either "STREAM", "DATAGRAM" or "RAW",
// for a new I2P tunnel with name id, using the cypher keys specified, with the
// I2CP/streaminglib-options as specified. Extra arguments can be specified by
// setting extra to something else than []string{}.
// This sam3 instance is now a session
func (sam *PrimarySession) newGenericSubSession(style, id string, extras []string) (net.Conn, error) {
return sam.newGenericSubSessionWithSignature(style, id, extras)
}
func (sam *PrimarySession) newGenericSubSessionWithSignature(style, id string, extras []string) (net.Conn, error) {
return sam.newGenericSubSessionWithSignatureAndPorts(style, id, "0", "0", extras)
}
// Creates a new session with the style of either "STREAM", "DATAGRAM" or "RAW",
// for a new I2P tunnel with name id, using the cypher keys specified, with the
// I2CP/streaminglib-options as specified. Extra arguments can be specified by
// setting extra to something else than []string{}.
// This sam3 instance is now a session
func (sam *PrimarySession) newGenericSubSessionWithSignatureAndPorts(style, id, from, to string, extras []string) (net.Conn, error) {
conn := sam.conn
fp := ""
tp := ""
if from != "0" {
fp = " FROM_PORT=" + from
}
if to != "0" {
tp = " TO_PORT=" + to
}
scmsg := []byte("SESSION ADD STYLE=" + style + fp + tp + " ID=" + id + " " + strings.Join(extras, " ") + "\n")
for m, i := 0, 0; m != len(scmsg); i++ {
if i == 15 {
conn.Close()
return nil, errors.New("writing to SAM failed")
}
n, err := conn.Write(scmsg[m:])
if err != nil {
conn.Close()
return nil, err
}
m += n
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
conn.Close()
return nil, err
}
text := string(buf[:n])
if strings.HasPrefix(text, session_ADDOK) {
//if sam.keys.String() != text[len(session_ADDOK):len(text)-1] {
//conn.Close()
//return nil, errors.New("SAMv3 created a tunnel with keys other than the ones we asked it for")
//}
return conn, nil //&StreamSession{id, conn, keys, nil, sync.RWMutex{}, nil}, nil
} else if text == session_DUPLICATE_ID {
conn.Close()
return nil, errors.New("Duplicate tunnel name")
} else if text == session_DUPLICATE_DEST {
conn.Close()
return nil, errors.New("Duplicate destination")
} else if text == session_INVALID_KEY {
conn.Close()
return nil, errors.New("Invalid key")
} else if strings.HasPrefix(text, session_I2P_ERROR) {
conn.Close()
return nil, errors.New("I2P error " + text[len(session_I2P_ERROR):])
} else {
conn.Close()
return nil, errors.New("Unable to parse SAMv3 reply: " + text)
}
}
// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *PrimarySession) NewStreamSubSession(id string) (*StreamSession, error) {
conn, err := sam.newGenericSubSession("STREAM", id, []string{})
if err != nil {
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
}
// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *PrimarySession) NewUniqueStreamSubSession(id string) (*StreamSession, error) {
conn, err := sam.newGenericSubSession("STREAM", id, []string{})
if err != nil {
return nil, err
}
return &StreamSession{sam.Config.I2PConfig.Sam(), id, 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
// specified. See the I2P documentation for a full list of options.
func (sam *PrimarySession) NewStreamSubSessionWithPorts(id, from, to string) (*StreamSession, error) {
conn, err := sam.newGenericSubSessionWithSignatureAndPorts("STREAM", id, from, to, []string{})
if err != nil {
return nil, err
}
return &StreamSession{sam.Config.I2PConfig.Sam(), id, conn, sam.keys, time.Duration(600 * time.Second), time.Now(), Sig_NONE, from, to}, nil
}
/*
func (s *PrimarySession) I2PListener(name string) (*StreamListener, error) {
listener, err := s.NewStreamSubSession(name)
if err != nil {
return nil, err
}
return listener.Listen()
}
*/
// Creates a new datagram session. udpPort is the UDP port SAM is listening on,
// and if you set it to zero, it will use SAMs standard UDP port.
func (s *PrimarySession) NewDatagramSubSession(id string, udpPort int) (*DatagramSession, error) {
if udpPort > 65335 || udpPort < 0 {
return nil, errors.New("udpPort needs to be in the intervall 0-65335")
}
if udpPort == 0 {
udpPort = 7655
}
lhost, _, err := net.SplitHostPort(s.conn.LocalAddr().String())
if err != nil {
s.Close()
return nil, err
}
lUDPAddr, err := net.ResolveUDPAddr("udp4", lhost+":0")
if err != nil {
return nil, err
}
udpconn, err := net.ListenUDP("udp4", lUDPAddr)
if err != nil {
return nil, err
}
rhost, _, err := net.SplitHostPort(s.conn.RemoteAddr().String())
if err != nil {
s.Close()
return nil, err
}
rUDPAddr, err := net.ResolveUDPAddr("udp4", rhost+":"+strconv.Itoa(udpPort))
if err != nil {
return nil, err
}
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
conn, err := s.newGenericSubSession("DATAGRAM", id, []string{"PORT=" + lport})
if err != nil {
return nil, err
}
return &DatagramSession{s.Config.I2PConfig.Sam(), id, conn, udpconn, s.keys, rUDPAddr, nil}, nil
}
// Creates a new raw session. udpPort is the UDP port SAM is listening on,
// and if you set it to zero, it will use SAMs standard UDP port.
func (s *PrimarySession) NewRawSubSession(id string, udpPort int) (*RawSession, error) {
if udpPort > 65335 || udpPort < 0 {
return nil, errors.New("udpPort needs to be in the intervall 0-65335")
}
if udpPort == 0 {
udpPort = 7655
}
lhost, _, err := net.SplitHostPort(s.conn.LocalAddr().String())
if err != nil {
s.Close()
return nil, err
}
lUDPAddr, err := net.ResolveUDPAddr("udp4", lhost+":0")
if err != nil {
return nil, err
}
udpconn, err := net.ListenUDP("udp4", lUDPAddr)
if err != nil {
return nil, err
}
rhost, _, err := net.SplitHostPort(s.conn.RemoteAddr().String())
if err != nil {
s.Close()
return nil, err
}
rUDPAddr, err := net.ResolveUDPAddr("udp4", rhost+":"+strconv.Itoa(udpPort))
if err != nil {
return nil, err
}
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
// conn, err := s.newGenericSubSession("RAW", id, s.keys, options, []string{"PORT=" + lport})
conn, err := s.newGenericSubSession("RAW", id, []string{"PORT=" + lport})
if err != nil {
return nil, err
}
return &RawSession{s.Config.I2PConfig.Sam(), id, conn, udpconn, s.keys, rUDPAddr}, nil
}

151
primary_datagram_test.go Normal file
View File

@@ -0,0 +1,151 @@
// +build nettest
package sam3
import (
"fmt"
"log"
"testing"
"time"
)
func Test_PrimaryDatagramServerClient(t *testing.T) {
if testing.Short() {
return
}
fmt.Println("Test_PrimaryDatagramServerClient")
earlysam, err := NewSAM(yoursam)
if err != nil {
t.Fail()
return
}
defer earlysam.Close()
keys, err := earlysam.NewKeys()
if err != nil {
t.Fail()
return
}
sam, err := earlysam.NewPrimarySession("PrimaryTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
t.Fail()
return
}
defer sam.Close()
// fmt.Println("\tServer: My address: " + keys.Addr().Base32())
fmt.Println("\tServer: Creating tunnel")
ds, err := sam.NewDatagramSubSession("PrimaryTunnel"+RandString(), 0)
if err != nil {
fmt.Println("Server: Failed to create tunnel: " + err.Error())
t.Fail()
return
}
defer ds.Close()
c, w := make(chan bool), make(chan bool)
go func(c, w chan (bool)) {
sam2, err := NewSAM(yoursam)
if err != nil {
c <- false
return
}
defer sam2.Close()
keys, err := sam2.NewKeys()
if err != nil {
c <- false
return
}
fmt.Println("\tClient: Creating tunnel")
ds2, err := sam2.NewDatagramSession("PRIMARYClientTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"}, 0)
if err != nil {
c <- false
return
}
defer ds2.Close()
// fmt.Println("\tClient: Servers address: " + ds.LocalAddr().Base32())
// fmt.Println("\tClient: Clients address: " + ds2.LocalAddr().Base32())
fmt.Println("\tClient: Tries to send primary to server")
for {
select {
default:
_, err = ds2.WriteTo([]byte("Hello primary-world! <3 <3 <3 <3 <3 <3"), ds.LocalAddr())
if err != nil {
fmt.Println("\tClient: Failed to send primary: " + err.Error())
c <- false
return
}
time.Sleep(5 * time.Second)
case <-w:
fmt.Println("\tClient: Sent primary, quitting.")
return
}
}
c <- true
}(c, w)
buf := make([]byte, 512)
fmt.Println("\tServer: ReadFrom() waiting...")
n, _, err := ds.ReadFrom(buf)
w <- true
if err != nil {
fmt.Println("\tServer: Failed to ReadFrom(): " + err.Error())
t.Fail()
return
}
fmt.Println("\tServer: Received primary: " + string(buf[:n]))
// fmt.Println("\tServer: Senders address was: " + saddr.Base32())
}
func ExamplePrimaryDatagramSession() {
// Creates a new PrimarySession, then creates a Datagram subsession on top of it
const samBridge = "127.0.0.1:7656"
earlysam, err := NewSAM(samBridge)
if err != nil {
fmt.Println(err.Error())
return
}
defer earlysam.Close()
keys, err := earlysam.NewKeys()
if err != nil {
fmt.Println(err.Error())
return
}
myself := keys.Addr()
sam, err := earlysam.NewPrimarySession("PrimaryTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
fmt.Println(err.Error())
return
}
defer sam.Close()
// See the example Option_* variables.
dg, err := sam.NewDatagramSubSession("DGTUN"+RandString(), 0)
if err != nil {
fmt.Println(err.Error())
return
}
defer dg.Close()
someone, err := earlysam.Lookup("zzz.i2p")
if err != nil {
fmt.Println(err.Error())
return
}
dg.WriteTo([]byte("Hello stranger!"), someone)
dg.WriteTo([]byte("Hello myself!"), myself)
buf := make([]byte, 31*1024)
n, _, err := dg.ReadFrom(buf)
if err != nil {
fmt.Println(err.Error())
return
}
log.Println("Got message: '" + string(buf[:n]) + "'")
fmt.Println("Got message: " + string(buf[:n]))
return
// Output:
//Got message: Hello myself!
}

309
primary_stream_test.go Normal file
View File

@@ -0,0 +1,309 @@
// +build nettest
package sam3
import (
"fmt"
"log"
"strings"
"testing"
"time"
"github.com/eyedeekay/sam3/i2pkeys"
)
func Test_PrimaryStreamingDial(t *testing.T) {
if testing.Short() {
return
}
fmt.Println("Test_PrimaryStreamingDial")
earlysam, err := NewSAM(yoursam)
if err != nil {
t.Fail()
return
}
defer earlysam.Close()
keys, err := earlysam.NewKeys()
if err != nil {
t.Fail()
return
}
sam, err := earlysam.NewPrimarySession("PrimaryTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
t.Fail()
return
}
defer sam.Close()
fmt.Println("\tBuilding tunnel")
ss, err := sam.NewStreamSubSession("primaryStreamTunnel")
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
defer ss.Close()
fmt.Println("\tNotice: This may fail if your I2P node is not well integrated in the I2P network.")
fmt.Println("\tLooking up i2p-projekt.i2p")
forumAddr, err := earlysam.Lookup("i2p-projekt.i2p")
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
fmt.Println("\tDialing i2p-projekt.i2p(", forumAddr.Base32(), forumAddr.DestHash().Hash(), ")")
conn, err := ss.DialI2P(forumAddr)
if err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
defer conn.Close()
fmt.Println("\tSending HTTP GET /")
if _, err := conn.Write([]byte("GET /\n")); err != nil {
fmt.Println(err.Error())
t.Fail()
return
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if !strings.Contains(strings.ToLower(string(buf[:n])), "http") && !strings.Contains(strings.ToLower(string(buf[:n])), "html") {
fmt.Printf("\tProbably failed to StreamSession.DialI2P(i2p-projekt.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
} else {
fmt.Println("\tRead HTTP/HTML from i2p-projekt.i2p")
}
}
func Test_PrimaryStreamingServerClient(t *testing.T) {
if testing.Short() {
return
}
fmt.Println("Test_StreamingServerClient")
earlysam, err := NewSAM(yoursam)
if err != nil {
t.Fail()
return
}
defer earlysam.Close()
keys, err := earlysam.NewKeys()
if err != nil {
t.Fail()
return
}
sam, err := earlysam.NewPrimarySession("PrimaryServerClientTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
t.Fail()
return
}
defer sam.Close()
fmt.Println("\tServer: Creating tunnel")
ss, err := sam.NewUniqueStreamSubSession("primaryExampleServerTun")
if err != nil {
return
}
defer ss.Close()
time.Sleep(time.Second * 10)
c, w := make(chan bool), make(chan bool)
go func(c, w chan (bool)) {
if !(<-w) {
return
}
/*sam2, err := NewSAM(yoursam)
if err != nil {
c <- false
return
}
defer sam2.Close()
keys, err := sam2.NewKeys()
if err != nil {
c <- false
return
}*/
fmt.Println("\tClient: Creating tunnel")
ss2, err := sam.NewStreamSubSession("primaryExampleClientTun")
if err != nil {
c <- false
return
}
defer ss2.Close()
fmt.Println("\tClient: Connecting to server")
conn, err := ss2.DialI2P(ss.Addr())
if err != nil {
c <- false
return
}
fmt.Println("\tClient: Connected to tunnel")
defer conn.Close()
_, err = conn.Write([]byte("Hello world <3 <3 <3 <3 <3 <3"))
if err != nil {
c <- false
return
}
c <- true
}(c, w)
l, err := ss.Listen()
if err != nil {
fmt.Println("ss.Listen(): " + err.Error())
t.Fail()
w <- false
return
}
defer l.Close()
w <- true
fmt.Println("\tServer: Accept()ing on tunnel")
conn, err := l.Accept()
if err != nil {
t.Fail()
fmt.Println("Failed to Accept(): " + err.Error())
return
}
defer conn.Close()
buf := make([]byte, 512)
n, err := conn.Read(buf)
fmt.Printf("\tClient exited successfully: %t\n", <-c)
fmt.Println("\tServer: received from Client: " + string(buf[:n]))
}
func ExamplePrimaryStreamSession() {
// Creates a new StreamingSession, dials to idk.i2p and gets a SAMConn
// which behaves just like a normal net.Conn.
const samBridge = "127.0.0.1:7656"
earlysam, err := NewSAM(yoursam)
if err != nil {
log.Fatal(err.Error())
return
}
defer earlysam.Close()
keys, err := earlysam.NewKeys()
if err != nil {
log.Fatal(err.Error())
return
}
sam, err := earlysam.NewPrimarySession("PrimaryServerClientTunnel", 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()
// 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 {
fmt.Println(err.Error())
return
}
defer conn.Close()
fmt.Println("Sending HTTP GET /")
if _, err := conn.Write([]byte("GET /\n")); err != nil {
fmt.Println(err.Error())
return
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if !strings.Contains(strings.ToLower(string(buf[:n])), "http") && !strings.Contains(strings.ToLower(string(buf[:n])), "html") {
fmt.Printf("Probably failed to StreamSession.DialI2P(idk.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
log.Printf("Probably failed to StreamSession.DialI2P(idk.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
} else {
fmt.Println("Read HTTP/HTML from idk.i2p")
log.Println("Read HTTP/HTML from idk.i2p")
}
return
// Output:
//Sending HTTP GET /
//Read HTTP/HTML from idk.i2p
}
func ExamplePrimaryStreamListener() {
// One server Accept()ing on a StreamListener, and one client that Dials
// through I2P to the server. Server writes "Hello world!" through a SAMConn
// (which implements net.Conn) and the client prints the message.
const samBridge = "127.0.0.1:7656"
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()
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 {
fmt.Println(err.Error())
return
}
defer ss.Close()
l, err := ss.Listen()
if err != nil {
fmt.Println(err.Error())
return
}
conn, err := l.Accept()
if err != nil {
fmt.Println(err.Error())
return
}
conn.Write([]byte("Hello world!"))
<-quit // waits for client to die, for example only
// Output:
//Hello world!
}

22
raw.go
View File

@@ -6,10 +6,8 @@ import (
"net"
"strconv"
"time"
)
import (
. "github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/sam3/i2pkeys"
)
// The RawSession provides no authentication of senders, and there is no sender
@@ -19,17 +17,17 @@ import (
// that is needed. Raw datagrams may be at most 32 kB in size. There is no
// overhead of authentication, which is the reason to use this..
type RawSession struct {
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam bridge
udpconn *net.UDPConn // used to deliver datagrams
keys I2PKeys // i2p destination keys
rUDPAddr *net.UDPAddr // the SAM bridge UDP-port
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam bridge
udpconn *net.UDPConn // used to deliver datagrams
keys i2pkeys.I2PKeys // i2p destination keys
rUDPAddr *net.UDPAddr // the SAM bridge UDP-port
}
// Creates a new raw session. udpPort is the UDP port SAM is listening on,
// and if you set it to zero, it will use SAMs standard UDP port.
func (s *SAM) NewRawSession(id string, keys I2PKeys, options []string, udpPort int) (*RawSession, error) {
func (s *SAM) NewRawSession(id string, keys i2pkeys.I2PKeys, options []string, udpPort int) (*RawSession, error) {
if udpPort > 65335 || udpPort < 0 {
return nil, errors.New("udpPort needs to be in the intervall 0-65335")
}
@@ -87,7 +85,7 @@ func (s *RawSession) Read(b []byte) (n int, err error) {
// Sends one raw datagram to the destination specified. At the time of writing,
// maximum size is 32 kilobyte, but this may change in the future.
func (s *RawSession) WriteTo(b []byte, addr I2PAddr) (n int, err error) {
func (s *RawSession) WriteTo(b []byte, addr i2pkeys.I2PAddr) (n int, err error) {
header := []byte("3.0 " + s.id + " " + addr.String() + "\n")
msg := append(header, b...)
n, err = s.udpconn.WriteToUDP(msg, s.rUDPAddr)
@@ -105,7 +103,7 @@ func (s *RawSession) Close() error {
}
// Returns the local I2P destination of the RawSession.
func (s *RawSession) LocalAddr() I2PAddr {
func (s *RawSession) LocalAddr() i2pkeys.I2PAddr {
return s.keys.Addr()
}

View File

@@ -5,10 +5,8 @@ import (
"bytes"
"errors"
"strings"
)
import (
. "github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/sam3/i2pkeys"
)
type SAMResolver struct {
@@ -33,19 +31,19 @@ func NewFullSAMResolver(address string) (*SAMResolver, error) {
// Performs a lookup, probably this order: 1) routers known addresses, cached
// addresses, 3) by asking peers in the I2P network.
func (sam *SAMResolver) Resolve(name string) (I2PAddr, error) {
if _, err := sam.conn.Write([]byte("NAMING LOOKUP NAME=" + name + "\n")); err != nil {
func (sam *SAMResolver) Resolve(name string) (i2pkeys.I2PAddr, error) {
if _, err := sam.conn.Write([]byte("NAMING LOOKUP NAME=" + name + "\r\n")); err != nil {
sam.Close()
return I2PAddr(""), err
return i2pkeys.I2PAddr(""), err
}
buf := make([]byte, 4096)
n, err := sam.conn.Read(buf)
if err != nil {
sam.Close()
return I2PAddr(""), err
return i2pkeys.I2PAddr(""), err
}
if n <= 13 || !strings.HasPrefix(string(buf[:n]), "NAMING REPLY ") {
return I2PAddr(""), errors.New("Failed to parse.")
return i2pkeys.I2PAddr(""), errors.New("Failed to parse.")
}
s := bufio.NewScanner(bytes.NewReader(buf[13:n]))
s.Split(bufio.ScanWords)
@@ -53,6 +51,7 @@ func (sam *SAMResolver) Resolve(name string) (I2PAddr, error) {
errStr := ""
for s.Scan() {
text := s.Text()
//log.Println("SAM3", text)
if text == "RESULT=OK" {
continue
} else if text == "RESULT=INVALID_KEY" {
@@ -62,12 +61,12 @@ func (sam *SAMResolver) Resolve(name string) (I2PAddr, error) {
} else if text == "NAME="+name {
continue
} else if strings.HasPrefix(text, "VALUE=") {
return I2PAddr(text[6:]), nil
return i2pkeys.I2PAddr(text[6:]), nil
} else if strings.HasPrefix(text, "MESSAGE=") {
errStr += " " + text[8:]
} else {
continue
}
}
return I2PAddr(""), errors.New(errStr)
return i2pkeys.I2PAddr(""), errors.New(errStr)
}

71
sam3.go
View File

@@ -6,22 +6,26 @@ import (
"bytes"
"errors"
"io"
"math/rand"
"net"
"os"
"strings"
"github.com/eyedeekay/sam3/i2pkeys"
)
import (
. "github.com/eyedeekay/ramp/emit"
. "github.com/eyedeekay/sam3/i2pkeys"
)
// Used for controlling I2Ps SAMv3.
type SAM struct {
//address string
address string
conn net.Conn
resolver *SAMResolver
Config SAMEmit
keys *i2pkeys.I2PKeys
sigType int
}
const (
@@ -41,6 +45,17 @@ const (
Sig_EdDSA_SHA512_Ed25519 = "SIGNATURE_TYPE=EdDSA_SHA512_Ed25519"
)
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
func RandString() string {
n := 4
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
// Creates a new controller for the I2P routers SAM bridge.
func NewSAM(address string) (*SAM, error) {
var s SAM
@@ -49,7 +64,7 @@ func NewSAM(address string) (*SAM, error) {
if err != nil {
return nil, err
}
if _, err := conn.Write(s.Config.HelloBytes()); err != nil {
if _, err := conn.Write(s.Config.HelloBytes()); err != nil {
conn.Close()
return nil, err
}
@@ -62,7 +77,7 @@ func NewSAM(address string) (*SAM, error) {
if strings.Contains(string(buf[:n]), "HELLO REPLY RESULT=OK") {
s.Config.I2PConfig.SetSAMAddress(address)
s.conn = conn
s.Config.I2PConfig.DestinationKeys = nil
//s.Config.I2PConfig.DestinationKeys = nil
s.resolver, err = NewSAMResolver(&s)
if err != nil {
return nil, err
@@ -78,29 +93,29 @@ func NewSAM(address string) (*SAM, error) {
}
}
func (sam *SAM) Keys() (k *I2PKeys) {
func (sam *SAM) Keys() (k *i2pkeys.I2PKeys) {
//TODO: copy them?
k = sam.Config.I2PConfig.DestinationKeys
k = &sam.Config.I2PConfig.DestinationKeys
return
}
// read public/private keys from an io.Reader
func (sam *SAM) ReadKeys(r io.Reader) (err error) {
var keys I2PKeys
keys, err = LoadKeysIncompat(r)
var keys i2pkeys.I2PKeys
keys, err = i2pkeys.LoadKeysIncompat(r)
if err == nil {
sam.Config.I2PConfig.DestinationKeys = &keys
sam.Config.I2PConfig.DestinationKeys = keys
}
return
}
// if keyfile fname does not exist
func (sam *SAM) EnsureKeyfile(fname string) (keys I2PKeys, err error) {
func (sam *SAM) EnsureKeyfile(fname string) (keys i2pkeys.I2PKeys, err error) {
if fname == "" {
// transient
keys, err = sam.NewKeys()
if err == nil {
sam.Config.I2PConfig.DestinationKeys = &keys
sam.Config.I2PConfig.DestinationKeys = keys
}
} else {
// persistant
@@ -109,12 +124,12 @@ func (sam *SAM) EnsureKeyfile(fname string) (keys I2PKeys, err error) {
// make the keys
keys, err = sam.NewKeys()
if err == nil {
sam.Config.I2PConfig.DestinationKeys = &keys
sam.Config.I2PConfig.DestinationKeys = keys
// save keys
var f io.WriteCloser
f, err = os.OpenFile(fname, os.O_WRONLY|os.O_CREATE, 0600)
if err == nil {
err = StoreKeysIncompat(keys, f)
err = i2pkeys.StoreKeysIncompat(keys, f)
f.Close()
}
}
@@ -123,9 +138,9 @@ func (sam *SAM) EnsureKeyfile(fname string) (keys I2PKeys, err error) {
var f *os.File
f, err = os.Open(fname)
if err == nil {
keys, err = LoadKeysIncompat(f)
keys, err = i2pkeys.LoadKeysIncompat(f)
if err == nil {
sam.Config.I2PConfig.DestinationKeys = &keys
sam.Config.I2PConfig.DestinationKeys = keys
}
}
}
@@ -136,18 +151,18 @@ func (sam *SAM) EnsureKeyfile(fname string) (keys I2PKeys, err error) {
// Creates the I2P-equivalent of an IP address, that is unique and only the one
// who has the private keys can send messages from. The public keys are the I2P
// desination (the address) that anyone can send messages to.
func (sam *SAM) NewKeys(sigType ...string) (I2PKeys, error) {
func (sam *SAM) NewKeys(sigType ...string) (i2pkeys.I2PKeys, error) {
sigtmp := ""
if len(sigType) > 0 {
sigtmp = sigType[0]
}
if _, err := sam.conn.Write([]byte("DEST GENERATE " + sigtmp + "\n")); err != nil {
return I2PKeys{}, err
return i2pkeys.I2PKeys{}, err
}
buf := make([]byte, 8192)
n, err := sam.conn.Read(buf)
if err != nil {
return I2PKeys{}, err
return i2pkeys.I2PKeys{}, err
}
s := bufio.NewScanner(bytes.NewReader(buf[:n]))
s.Split(bufio.ScanWords)
@@ -164,7 +179,7 @@ func (sam *SAM) NewKeys(sigType ...string) (I2PKeys, error) {
} else if strings.HasPrefix(text, "PRIV=") {
priv = text[5:]
} else {
return I2PKeys{}, errors.New("Failed to parse keys.")
return i2pkeys.I2PKeys{}, errors.New("Failed to parse keys.")
}
}
return NewKeys(I2PAddr(pub), priv), nil
@@ -172,7 +187,7 @@ func (sam *SAM) NewKeys(sigType ...string) (I2PKeys, error) {
// Performs a lookup, probably this order: 1) routers known addresses, cached
// addresses, 3) by asking peers in the I2P network.
func (sam *SAM) Lookup(name string) (I2PAddr, error) {
func (sam *SAM) Lookup(name string) (i2pkeys.I2PAddr, error) {
return sam.resolver.Resolve(name)
}
@@ -181,11 +196,11 @@ func (sam *SAM) Lookup(name string) (I2PAddr, error) {
// I2CP/streaminglib-options as specified. Extra arguments can be specified by
// setting extra to something else than []string{}.
// This sam3 instance is now a session
func (sam *SAM) newGenericSession(style, id string, keys I2PKeys, options []string, extras []string) (net.Conn, error) {
func (sam *SAM) newGenericSession(style, id string, keys i2pkeys.I2PKeys, options []string, extras []string) (net.Conn, error) {
return sam.newGenericSessionWithSignature(style, id, keys, Sig_NONE, options, extras)
}
func (sam *SAM) newGenericSessionWithSignature(style, id string, keys I2PKeys, sigType string, options []string, extras []string) (net.Conn, error) {
func (sam *SAM) newGenericSessionWithSignature(style, id string, keys i2pkeys.I2PKeys, sigType string, options []string, extras []string) (net.Conn, error) {
return sam.newGenericSessionWithSignatureAndPorts(style, id, "0", "0", keys, sigType, options, extras)
}
@@ -194,7 +209,7 @@ func (sam *SAM) newGenericSessionWithSignature(style, id string, keys I2PKeys, s
// I2CP/streaminglib-options as specified. Extra arguments can be specified by
// setting extra to something else than []string{}.
// This sam3 instance is now a session
func (sam *SAM) newGenericSessionWithSignatureAndPorts(style, id, from, to string, keys I2PKeys, sigType string, options []string, extras []string) (net.Conn, error) {
func (sam *SAM) newGenericSessionWithSignatureAndPorts(style, id, from, to string, keys i2pkeys.I2PKeys, sigType string, options []string, extras []string) (net.Conn, error) {
optStr := ""
for _, opt := range options {
@@ -202,7 +217,15 @@ func (sam *SAM) newGenericSessionWithSignatureAndPorts(style, id, from, to strin
}
conn := sam.conn
scmsg := []byte("SESSION CREATE STYLE=" + style + " FROM_PORT=" + from + " TO_PORT=" + to + " ID=" + id + " DESTINATION=" + keys.String() + " " + sigType + " " + optStr + strings.Join(extras, " ") + "\n")
fp := ""
tp := ""
if from != "0" {
fp = " FROM_PORT=" + from
}
if to != "0" {
tp = " TO_PORT=" + to
}
scmsg := []byte("SESSION CREATE STYLE=" + style + fp + tp + " ID=" + id + " DESTINATION=" + keys.String() + " " + optStr + strings.Join(extras, " ") + "\n")
for m, i := 0, 0; m != len(scmsg); i++ {
if i == 15 {
conn.Close()

View File

@@ -11,18 +11,16 @@ import (
"strconv"
"strings"
"time"
)
import (
. "github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/sam3/i2pkeys"
)
// Represents a streaming session.
type StreamSession struct {
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam
keys I2PKeys // i2p destination keys
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam
keys i2pkeys.I2PKeys // i2p destination keys
Timeout time.Duration
Deadline time.Time
sigType string
@@ -30,6 +28,18 @@ type StreamSession struct {
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 {
return ss.from
}
@@ -52,18 +62,22 @@ func (ss *StreamSession) Close() error {
}
// Returns the I2P destination (the address) of the stream session
func (ss *StreamSession) Addr() I2PAddr {
func (ss *StreamSession) Addr() i2pkeys.I2PAddr {
return ss.keys.Addr()
}
func (ss *StreamSession) LocalAddr() net.Addr {
return ss.keys.Addr()
}
// Returns the keys associated with the stream session
func (ss *StreamSession) Keys() I2PKeys {
func (ss *StreamSession) Keys() i2pkeys.I2PKeys {
return ss.keys
}
// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewStreamSession(id string, keys I2PKeys, options []string) (*StreamSession, error) {
func (sam *SAM) NewStreamSession(id string, keys i2pkeys.I2PKeys, options []string) (*StreamSession, error) {
conn, err := sam.newGenericSession("STREAM", id, keys, options, []string{})
if err != nil {
return nil, err
@@ -73,7 +87,7 @@ func (sam *SAM) NewStreamSession(id string, keys I2PKeys, options []string) (*St
// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewStreamSessionWithSignature(id string, keys I2PKeys, options []string, sigType string) (*StreamSession, error) {
func (sam *SAM) NewStreamSessionWithSignature(id string, keys i2pkeys.I2PKeys, options []string, sigType string) (*StreamSession, error) {
conn, err := sam.newGenericSessionWithSignature("STREAM", id, keys, sigType, options, []string{})
if err != nil {
return nil, err
@@ -83,7 +97,7 @@ func (sam *SAM) NewStreamSessionWithSignature(id string, keys I2PKeys, options [
// Creates a new StreamSession with the I2CP- and streaminglib options as
// specified. See the I2P documentation for a full list of options.
func (sam *SAM) NewStreamSessionWithSignatureAndPorts(id, from, to string, keys I2PKeys, options []string, sigType string) (*StreamSession, error) {
func (sam *SAM) NewStreamSessionWithSignatureAndPorts(id, from, to string, keys i2pkeys.I2PKeys, options []string, sigType string) (*StreamSession, error) {
conn, err := sam.newGenericSessionWithSignatureAndPorts("STREAM", id, from, to, keys, sigType, options, []string{})
if err != nil {
return nil, err
@@ -92,14 +106,14 @@ func (sam *SAM) NewStreamSessionWithSignatureAndPorts(id, from, to string, keys
}
// lookup name, convienence function
func (s *StreamSession) Lookup(name string) (I2PAddr, error) {
func (s *StreamSession) Lookup(name string) (i2pkeys.I2PAddr, error) {
sam, err := NewSAM(s.samAddr)
if err == nil {
addr, err := sam.Lookup(name)
sam.Close()
return addr, err
}
return I2PAddr(""), err
return i2pkeys.I2PAddr(""), err
}
// context-aware dialer, eventually...
@@ -121,7 +135,7 @@ func (s *StreamSession) DialContextI2P(ctx context.Context, n, addr string) (*SA
}
}
i2paddr, err := NewI2PAddrFromString(addr)
i2paddr, err := i2pkeys.NewI2PAddrFromString(addr)
if err != nil {
return nil, err
}
@@ -163,7 +177,7 @@ func (s *StreamSession) deadline(ctx context.Context, now time.Time) (earliest t
// implement net.Dialer
func (s *StreamSession) Dial(n, addr string) (c net.Conn, err error) {
var i2paddr I2PAddr
var i2paddr i2pkeys.I2PAddr
var host string
host, _, err = net.SplitHostPort(addr)
if err == nil {
@@ -173,7 +187,7 @@ func (s *StreamSession) Dial(n, addr string) (c net.Conn, err error) {
i2paddr, err = s.Lookup(host)
} else {
// probably a destination
i2paddr = I2PAddr(host)
i2paddr = i2pkeys.I2PAddr(host)
}
if err == nil {
return s.DialI2P(i2paddr)
@@ -183,7 +197,7 @@ func (s *StreamSession) Dial(n, addr string) (c net.Conn, err error) {
}
// Dials to an I2P destination and returns a SAMConn, which implements a net.Conn.
func (s *StreamSession) DialI2P(addr I2PAddr) (*SAMConn, error) {
func (s *StreamSession) DialI2P(addr i2pkeys.I2PAddr) (*SAMConn, error) {
sam, err := NewSAM(s.samAddr)
if err != nil {
return nil, err
@@ -248,7 +262,7 @@ type StreamListener struct {
// our session id
id string
// our local address for this sam socket
laddr I2PAddr
laddr i2pkeys.I2PAddr
}
func (l *StreamListener) From() string {
@@ -325,7 +339,7 @@ func (l *StreamListener) AcceptI2P() (*SAMConn, error) {
dest = strings.Trim(dest, "\n")
return &SAMConn{
laddr: l.laddr,
raddr: I2PAddr(dest),
raddr: i2pkeys.I2PAddr(dest),
conn: s.conn,
}, nil
} else {

View File

@@ -4,12 +4,11 @@ package sam3
import (
"fmt"
"log"
"strings"
"testing"
)
import (
. "github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/sam3/i2pkeys"
)
func Test_StreamingDial(t *testing.T) {
@@ -31,7 +30,7 @@ func Test_StreamingDial(t *testing.T) {
return
}
fmt.Println("\tBuilding tunnel")
ss, err := sam.NewStreamSession("streamTun", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
ss, err := sam.NewStreamSession("streamTun", keys, []string{"inbound.length=1", "outbound.length=1", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
fmt.Println(err.Error())
t.Fail()
@@ -45,7 +44,7 @@ func Test_StreamingDial(t *testing.T) {
t.Fail()
return
}
fmt.Println("\tDialing i2p-projekt.i2p")
fmt.Println("\tDialing i2p-projekt.i2p(", forumAddr.Base32(), forumAddr.DestHash().Hash(), ")")
conn, err := ss.DialI2P(forumAddr)
if err != nil {
fmt.Println(err.Error())
@@ -151,7 +150,7 @@ func Test_StreamingServerClient(t *testing.T) {
}
func ExampleStreamSession() {
// Creates a new StreamingSession, dials to zzz.i2p and gets a SAMConn
// Creates a new StreamingSession, dials to idk.i2p and gets a SAMConn
// which behaves just like a normal net.Conn.
const samBridge = "127.0.0.1:7656"
@@ -173,7 +172,7 @@ func ExampleStreamSession() {
fmt.Println(err.Error())
return
}
someone, err := sam.Lookup("zzz.i2p")
someone, err := sam.Lookup("idk.i2p")
if err != nil {
fmt.Println(err.Error())
return
@@ -193,15 +192,17 @@ func ExampleStreamSession() {
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if !strings.Contains(strings.ToLower(string(buf[:n])), "http") && !strings.Contains(strings.ToLower(string(buf[:n])), "html") {
fmt.Printf("Probably failed to StreamSession.DialI2P(zzz.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
fmt.Printf("Probably failed to StreamSession.DialI2P(idk.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
log.Printf("Probably failed to StreamSession.DialI2P(idk.i2p)? It replied %d bytes, but nothing that looked like http/html", n)
} else {
fmt.Println("Read HTTP/HTML from zzz.i2p")
fmt.Println("Read HTTP/HTML from idk.i2p")
log.Println("Read HTTP/HTML from idk.i2p")
}
return
// Output:
//Sending HTTP GET /
//Read HTTP/HTML from zzz.i2p
//Read HTTP/HTML from idk.i2p
}
func ExampleStreamListener() {
@@ -226,7 +227,7 @@ func ExampleStreamListener() {
quit := make(chan bool)
// Client connecting to the server
go func(server I2PAddr) {
go func(server i2pkeys.I2PAddr) {
csam, err := NewSAM(samBridge)
if err != nil {
fmt.Println(err.Error())

View File

@@ -10,17 +10,30 @@ var (
"inbound.quantity=6", "outbound.quantity=6"}
// Suitable for shuffling a lot of traffic.
Options_Fat = []string{"inbound.length=3", "outbound.length=3",
Options_Large = []string{"inbound.length=3", "outbound.length=3",
"inbound.lengthVariance=1", "outbound.lengthVariance=1",
"inbound.backupQuantity=1", "outbound.backupQuantity=1",
"inbound.quantity=4", "outbound.quantity=4"}
// Suitable for shuffling a lot of traffic quickly with minimum
// anonymity. Uses 1 hop and multiple tunnels.
Options_Wide = []string{"inbound.length=1", "outbound.length=1",
"inbound.lengthVariance=1", "outbound.lengthVariance=1",
"inbound.backupQuantity=2", "outbound.backupQuantity=2",
"inbound.quantity=3", "outbound.quantity=3"}
// Suitable for shuffling medium amounts of traffic.
Options_Medium = []string{"inbound.length=3", "outbound.length=3",
"inbound.lengthVariance=1", "outbound.lengthVariance=1",
"inbound.backupQuantity=0", "outbound.backupQuantity=0",
"inbound.quantity=2", "outbound.quantity=2"}
// Sensible defaults for most people
Options_Default = []string{"inbound.length=3", "outbound.length=3",
"inbound.lengthVariance=0", "outbound.lengthVariance=0",
"inbound.backupQuantity=1", "outbound.backupQuantity=1",
"inbound.quantity=1", "outbound.quantity=1"}
// Suitable only for small dataflows, and very short lasting connections:
// You only have one tunnel in each direction, so if any of the nodes
// through which any of your two tunnels pass through go offline, there will