14 Commits

Author SHA1 Message Date
idk
089f97b99f update version again so go modules finds it. 2022-05-20 16:08:40 -04:00
idk
eea4aa60a7 fix import 2022-05-03 00:21:09 -04:00
idk
00fb462ecd add announcement. remove i2pkeys 2022-03-10 01:02:13 -05:00
idk
c167f8e26d add announcement. 2022-03-10 01:01:31 -05:00
idk
7d38382735 add a NetAddr interface which slightly extends net.Addr to use a port. This should make it easier to use fake ports in some apps. 2022-03-03 18:54:48 -05:00
idk
3c910e4b33 Network on Desthash should probably not have a pointer for a reciever 2022-03-03 18:18:56 -05:00
idk
ab0064e92e add helper to generate a DestHash from bytes, makes Bittorrent a little easier to do because of CompactIP's 2022-03-03 16:53:08 -05:00
idk
b41ee5a01f add helper to generate a DestHash from bytes, makes Bittorrent a little easier to do because of CompactIP's 2022-03-03 16:51:19 -05:00
idk
ba5ad234ee add helper to generate a DestHash from bytes, makes Bittorrent a little easier to do because of CompactIP's 2022-03-03 16:49:48 -05:00
idk
eb723e9492 add helper to generate a DestHash from bytes, makes Bittorrent a little easier to do because of CompactIP's 2022-03-03 16:45:52 -05:00
idk
45106d2b70 fix semver. Check router type to determine whether to send PRIMARY or MASTER to SAM session 2022-01-10 11:19:39 -05:00
idk
0e87ddfa4b Bump and create a tag for @allhailjarjar's checkin. 2021-10-29 16:25:10 -04:00
idk
9e3532c81b Merge pull request #8 from allhailjarjar/master
Readme update and code cleanup from @allhailjarjar approved @eyedeekay
2021-10-29 16:19:21 -04:00
allhailjarjar666
9a162e9502 Readme update and code cleanup 2021-10-04 21:00:03 -04:00
23 changed files with 203 additions and 510 deletions

View File

@@ -1,6 +1,6 @@
USER_GH=eyedeekay
VERSION=0.33.001
VERSION=0.33.2
packagename=sam3
echo:

View File

@@ -1,5 +1,17 @@
# README #
## !!IMPORTANT!!
In the next version, I'll be moving the `i2pkeys` directory to it's own repository
so I can avoid import cycle headaches. Please migrate to the new `i2pkeys` repository
before upgrading your sam3 dependencies. You can probably do this by running:
```sh
find . -name '*.go' -exec sed -i 's|github.com/eyedeekay/sam3/i2pkeys|github.com/eyedeekay/i2pkeys|g' {} \;
```
# README #
go library for the I2P [SAMv3.0](https://geti2p.net/en/docs/api/samv3) bridge, used to build anonymous/pseudonymous end-to-end encrypted sockets.
This library is much better than ccondom (that use BOB), much more stable and much easier to maintain.
@@ -23,7 +35,7 @@ This library is much better than ccondom (that use BOB), much more stable and mu
**Does not work:**
* Everything works! :D
* Stream Forwarding
* Probably needs some real-world testing
## Documentation ##
@@ -38,16 +50,17 @@ This library is much better than ccondom (that use BOB), much more stable and mu
package main
import (
"github.com/majestrate/i2p-tools/sam3"
"github.com/eyedeekay/sam3"
"github.com/eyedeekay/sam3/i2pkeys"
"fmt"
)
const yoursam = "127.0.0.1:7656" // sam bridge
func client(server I2PAddr) {
sam, _ := NewSAM(yoursam)
func client(server i2pkeys.I2PAddr) {
sam, _ := sam3.NewSAM(yoursam)
keys, _ := sam.NewKeys()
stream, _ := sam.NewStreamSession("clientTun", keys, Options_Small)
stream, _ := sam.NewStreamSession("clientTun", keys, sam3.Options_Small)
fmt.Println("Client: Connecting to " + server.Base32())
conn, _ := stream.DialI2P(server)
conn.Write([]byte("Hello world!"))
@@ -57,11 +70,9 @@ func client(server I2PAddr) {
func main() {
sam, _ := NewSAM(yoursam)
keys, _ := sam.NewKeys()
stream, _ := sam.NewStreamSession("serverTun", keys, Options_Medium)
stream, _ := sam.NewStreamSession("serverTun", keys, sam3.Options_Medium)
listener, _ := stream.Listen()
go client(keys.Addr())
stream, _ := sam.NewStreamSession("serverTun", keys, Options_Medium)
listener, _ := stream.Listen()
conn, _ := listener.Accept()
buf := make([]byte, 4096)
n, _ := conn.Read(buf)

View File

@@ -1,14 +1,15 @@
package sam3
import (
"github.com/eyedeekay/sam3/i2pkeys"
"net"
"time"
"github.com/eyedeekay/i2pkeys"
)
/*
import (
. "github.com/eyedeekay/sam3/i2pkeys"
. "github.com/eyedeekay/i2pkeys"
)
*/
// Implements net.Conn

View File

@@ -7,7 +7,7 @@ import (
"strconv"
"strings"
"github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/i2pkeys"
)
// I2PConfig is a struct which manages I2P configuration options
@@ -347,7 +347,7 @@ func (cfg *Config) DatagramSession() (session *DatagramSession, err error) {
// determine udp port
var portstr string
_, portstr, err = net.SplitHostPort(cfg.Addr)
if err == nil {
if IgnorePortError(err) == nil {
var port int
port, err = strconv.Atoi(portstr)
if err == nil && port > 0 {

View File

@@ -7,7 +7,7 @@ import (
"strconv"
"time"
"github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/i2pkeys"
)
// The DatagramSession implements net.PacketConn. It works almost like ordinary
@@ -56,6 +56,10 @@ func (s *SAM) NewDatagramSession(id string, keys i2pkeys.I2PKeys, options []stri
return nil, err
}
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
if err != nil {
s.Close()
return nil, err
}
conn, err := s.newGenericSession("DATAGRAM", id, keys, options, []string{"PORT=" + lport})
if err != nil {
return nil, err

View File

@@ -1,5 +1,3 @@
// +build nettest
package sam3
import (

11
emit.go
View File

@@ -3,6 +3,7 @@ package sam3
import (
"fmt"
"log"
"strings"
)
type SAMEmit struct {
@@ -96,3 +97,13 @@ func NewEmit(opts ...func(*SAMEmit) error) (*SAMEmit, error) {
}
return &emit, nil
}
func IgnorePortError(err error) error {
if err == nil {
return nil
}
if strings.Contains(err.Error(), "missing port in address") {
err = nil
}
return err
}

1
go.mod
View File

@@ -4,6 +4,7 @@ go 1.12
require (
github.com/eyedeekay/goSam v0.32.31-0.20210122211817-f97683379f23
github.com/eyedeekay/i2pkeys v0.0.0-20220310052025-204d4ae6dcae // indirect
github.com/google/renameio v1.0.0 // indirect
github.com/riobard/go-x25519 v0.0.0-20190716001027-10cc4d8d0b33
github.com/rogpeppe/go-internal v1.6.2 // indirect

2
go.sum
View File

@@ -12,6 +12,8 @@ github.com/eyedeekay/goSam v0.32.31-0.20210122211024-dddd8ea916d6 h1:seMFdfTWvmA
github.com/eyedeekay/goSam v0.32.31-0.20210122211024-dddd8ea916d6/go.mod h1:UgJnih/LpotwKriwVPOEa6yPDM2NDdVrKfLtS5DOLPE=
github.com/eyedeekay/goSam v0.32.31-0.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/i2pkeys v0.0.0-20220310052025-204d4ae6dcae h1:SwegHeaf4pkDMB24UltIvJlj2+nd06QUZAbs8BDyfjM=
github.com/eyedeekay/i2pkeys v0.0.0-20220310052025-204d4ae6dcae/go.mod h1:W9KCm9lqZ+Ozwl3dwcgnpPXAML97+I8Jiht7o5A8YBM=
github.com/eyedeekay/sam3 v0.32.32/go.mod h1:qRA9KIIVxbrHlkj+ZB+OoxFGFgdKeGp1vSgPw26eOVU=
github.com/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=

View File

@@ -6,8 +6,8 @@ import (
"net"
"os"
"github.com/eyedeekay/i2pkeys"
"github.com/eyedeekay/sam3"
"github.com/eyedeekay/sam3/i2pkeys"
)
func NetListener(name, samaddr, keyspath string) (net.Listener, error) {

View File

@@ -1,325 +0,0 @@
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
}

View File

@@ -1,21 +0,0 @@
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())
}

View File

@@ -9,7 +9,7 @@ import (
"strings"
"time"
"github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/i2pkeys"
)
const (
@@ -137,9 +137,7 @@ func (sam *PrimarySession) DialUDPI2P(network, laddr, raddr string) (*DatagramSe
func (s *PrimarySession) Lookup(name string) (a net.Addr, err error) {
var sam *SAM
if len(strings.Split(name, ":")) <= 1 {
name += ":0"
}
name = strings.Split(name, ":")[0]
sam, err = NewSAM(s.samAddr)
if err == nil {
defer sam.Close()
@@ -163,7 +161,7 @@ func (sam *PrimarySession) ResolveUDPAddr(network, dest string) (net.Addr, error
// 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{})
conn, err := sam.newGenericSession(PrimarySessionSwitch, id, keys, options, []string{})
if err != nil {
return nil, err
}
@@ -175,7 +173,7 @@ func (sam *SAM) NewPrimarySession(id string, keys i2pkeys.I2PKeys, options []str
// 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{})
conn, err := sam.newGenericSessionWithSignature(PrimarySessionSwitch, id, keys, sigType, options, []string{})
if err != nil {
return nil, err
}
@@ -329,6 +327,10 @@ func (s *PrimarySession) NewDatagramSubSession(id string, udpPort int) (*Datagra
return nil, err
}
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
if err != nil {
s.Close()
return nil, err
}
conn, err := s.newGenericSubSession("DATAGRAM", id, []string{"PORT=" + lport})
if err != nil {
return nil, err
@@ -368,6 +370,10 @@ func (s *PrimarySession) NewRawSubSession(id string, udpPort int) (*RawSession,
return nil, err
}
_, lport, err := net.SplitHostPort(udpconn.LocalAddr().String())
if err != nil {
s.Close()
return nil, err
}
// conn, err := s.newGenericSubSession("RAW", id, s.keys, options, []string{"PORT=" + lport})
conn, err := s.newGenericSubSession("RAW", id, []string{"PORT=" + lport})
if err != nil {

View File

@@ -1,5 +1,3 @@
// +build nettest
package sam3
import (

View File

@@ -1,5 +1,3 @@
// +build nettest
package sam3
import (
@@ -9,7 +7,7 @@ import (
"testing"
"time"
"github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/i2pkeys"
)
func Test_PrimaryStreamingDial(t *testing.T) {
@@ -99,7 +97,7 @@ func Test_PrimaryStreamingServerClient(t *testing.T) {
}
defer sam.Close()
fmt.Println("\tServer: Creating tunnel")
ss, err := sam.NewUniqueStreamSubSession("primaryExampleServerTun")
ss, err := sam.NewUniqueStreamSubSession("PrimaryServerClientTunnel")
if err != nil {
return
}
@@ -184,26 +182,13 @@ func ExamplePrimaryStreamSession() {
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"})
sam, err := earlysam.NewPrimarySession("PrimaryStreamSessionTunnel", keys, []string{"inbound.length=0", "outbound.length=0", "inbound.lengthVariance=0", "outbound.lengthVariance=0", "inbound.quantity=1", "outbound.quantity=1"})
if err != nil {
log.Fatal(err.Error())
return
}
defer sam.Close()
// 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)
conn, err := sam.Dial("tcp", "idk.i2p") //someone.Base32())
if err != nil {
fmt.Println(err.Error())
return
@@ -223,8 +208,6 @@ func ExamplePrimaryStreamSession() {
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
@@ -260,7 +243,7 @@ func ExamplePrimaryStreamListener() {
// Client connecting to the server
go func(server i2pkeys.I2PAddr) {
cs, err := sam.NewStreamSubSession("client_example")
cs, err := sam.NewUniqueStreamSubSession("PrimaryListenerTunnel")
if err != nil {
fmt.Println(err.Error())
quit <- false
@@ -284,7 +267,7 @@ func ExamplePrimaryStreamListener() {
quit <- true
}(keys.Addr()) // end of client
ss, err := sam.NewStreamSubSession("server_example")
ss, err := sam.NewUniqueStreamSubSession("PrimaryListenerTunnel")
if err != nil {
fmt.Println(err.Error())
return

2
raw.go
View File

@@ -7,7 +7,7 @@ import (
"strconv"
"time"
"github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/i2pkeys"
)
// The RawSession provides no authentication of senders, and there is no sender

View File

@@ -6,7 +6,7 @@ import (
"errors"
"strings"
"github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/i2pkeys"
)
type SAMResolver struct {

View File

@@ -11,11 +11,11 @@ import (
"os"
"strings"
"github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/i2pkeys"
)
import (
. "github.com/eyedeekay/sam3/i2pkeys"
. "github.com/eyedeekay/i2pkeys"
)
// Used for controlling I2Ps SAMv3.

View File

@@ -1,5 +1,3 @@
// +build nettest
package sam3
import (

109
stream.go
View File

@@ -6,13 +6,11 @@ import (
"context"
"errors"
"io"
"log"
"net"
"strconv"
"strings"
"time"
"github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/i2pkeys"
)
// Represents a streaming session.
@@ -180,7 +178,7 @@ func (s *StreamSession) Dial(n, addr string) (c net.Conn, err error) {
var i2paddr i2pkeys.I2PAddr
var host string
host, _, err = net.SplitHostPort(addr)
if err == nil {
if err = IgnorePortError(err); err == nil {
// check for name
if strings.HasSuffix(host, ".b32.i2p") || strings.HasSuffix(host, ".i2p") {
// name lookup
@@ -255,106 +253,3 @@ func (s *StreamSession) Listen() (*StreamListener, error) {
laddr: s.keys.Addr(),
}, nil
}
type StreamListener struct {
// parent stream session
session *StreamSession
// our session id
id string
// our local address for this sam socket
laddr i2pkeys.I2PAddr
}
func (l *StreamListener) From() string {
return l.session.from
}
func (l *StreamListener) To() string {
return l.session.to
}
// get our address
// implements net.Listener
func (l *StreamListener) Addr() net.Addr {
return l.laddr
}
// implements net.Listener
func (l *StreamListener) Close() error {
return l.session.Close()
}
// implements net.Listener
func (l *StreamListener) Accept() (net.Conn, error) {
return l.AcceptI2P()
}
func ExtractPairString(input, value string) string {
parts := strings.Split(input, " ")
for _, part := range parts {
if strings.HasPrefix(part, value) {
kv := strings.SplitN(input, "=", 2)
if len(kv) == 2 {
return kv[1]
}
}
}
return ""
}
func ExtractPairInt(input, value string) int {
rv, err := strconv.Atoi(ExtractPairString(input, value))
if err != nil {
return 0
}
return rv
}
func ExtractDest(input string) string {
return strings.Split(input, " ")[0]
}
// accept a new inbound connection
func (l *StreamListener) AcceptI2P() (*SAMConn, error) {
s, err := NewSAM(l.session.samAddr)
if err == nil {
// we connected to sam
// send accept() command
_, err = io.WriteString(s.conn, "STREAM ACCEPT ID="+l.id+" SILENT=false\n")
// read reply
rd := bufio.NewReader(s.conn)
// read first line
line, err := rd.ReadString(10)
log.Println(line)
if err == nil {
if strings.HasPrefix(line, "STREAM STATUS RESULT=OK") {
// we gud read destination line
destline, err := rd.ReadString(10)
log.Println(destline)
if err == nil {
dest := ExtractDest(destline)
l.session.from = ExtractPairString(destline, "FROM_PORT")
l.session.to = ExtractPairString(destline, "TO_PORT")
// return wrapped connection
dest = strings.Trim(dest, "\n")
return &SAMConn{
laddr: l.laddr,
raddr: i2pkeys.I2PAddr(dest),
conn: s.conn,
}, nil
} else {
s.Close()
return nil, err
}
} else {
s.Close()
return nil, errors.New("invalid sam line: " + line)
}
} else {
s.Close()
return nil, err
}
}
s.Close()
return nil, err
}

116
streamListener.go Normal file
View File

@@ -0,0 +1,116 @@
package sam3
import (
"bufio"
"errors"
"io"
"log"
"net"
"strconv"
"strings"
"github.com/eyedeekay/i2pkeys"
)
type StreamListener struct {
// parent stream session
session *StreamSession
// our session id
id string
// our local address for this sam socket
laddr i2pkeys.I2PAddr
}
func (l *StreamListener) From() string {
return l.session.from
}
func (l *StreamListener) To() string {
return l.session.to
}
// get our address
// implements net.Listener
func (l *StreamListener) Addr() net.Addr {
return l.laddr
}
// implements net.Listener
func (l *StreamListener) Close() error {
return l.session.Close()
}
// implements net.Listener
func (l *StreamListener) Accept() (net.Conn, error) {
return l.AcceptI2P()
}
func ExtractPairString(input, value string) string {
parts := strings.Split(input, " ")
for _, part := range parts {
if strings.HasPrefix(part, value) {
kv := strings.SplitN(input, "=", 2)
if len(kv) == 2 {
return kv[1]
}
}
}
return ""
}
func ExtractPairInt(input, value string) int {
rv, err := strconv.Atoi(ExtractPairString(input, value))
if err != nil {
return 0
}
return rv
}
func ExtractDest(input string) string {
return strings.Split(input, " ")[0]
}
// accept a new inbound connection
func (l *StreamListener) AcceptI2P() (*SAMConn, error) {
s, err := NewSAM(l.session.samAddr)
if err == nil {
// we connected to sam
// send accept() command
_, err = io.WriteString(s.conn, "STREAM ACCEPT ID="+l.id+" SILENT=false\n")
// read reply
rd := bufio.NewReader(s.conn)
// read first line
line, err := rd.ReadString(10)
log.Println(line)
if err == nil {
if strings.HasPrefix(line, "STREAM STATUS RESULT=OK") {
// we gud read destination line
destline, err := rd.ReadString(10)
log.Println(destline)
if err == nil {
dest := ExtractDest(destline)
l.session.from = ExtractPairString(destline, "FROM_PORT")
l.session.to = ExtractPairString(destline, "TO_PORT")
// return wrapped connection
dest = strings.Trim(dest, "\n")
return &SAMConn{
laddr: l.laddr,
raddr: i2pkeys.I2PAddr(dest),
conn: s.conn,
}, nil
} else {
s.Close()
return nil, err
}
} else {
s.Close()
return nil, errors.New("invalid sam line: " + line)
}
} else {
s.Close()
return nil, err
}
}
s.Close()
return nil, err
}

View File

@@ -1,5 +1,3 @@
// +build nettest
package sam3
import (
@@ -8,7 +6,7 @@ import (
"strings"
"testing"
"github.com/eyedeekay/sam3/i2pkeys"
"github.com/eyedeekay/i2pkeys"
)
func Test_StreamingDial(t *testing.T) {

View File

@@ -1,5 +1,7 @@
package sam3
import "net/http"
// Examples and suggestions for options when creating sessions.
var (
// Suitable options if you are shuffling A LOT of traffic. If unused, this
@@ -51,3 +53,18 @@ var (
"inbound.backupQuantity=0", "outbound.backupQuantity=0",
"inbound.quantity=2", "outbound.quantity=2"}
)
func PrimarySessionString() string {
_, err := http.Get("http://127.0.0.1:7070")
if err != nil {
_, err := http.Get("http://127.0.0.1:7657")
if err != nil {
return "MASTER"
}
return "PRIMARY"
}
return "MASTER"
}
var PrimarySessionSwitch string = PrimarySessionString()