Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
089f97b99f | ||
![]() |
eea4aa60a7 | ||
![]() |
00fb462ecd | ||
![]() |
c167f8e26d | ||
![]() |
7d38382735 | ||
![]() |
3c910e4b33 | ||
![]() |
ab0064e92e | ||
![]() |
b41ee5a01f | ||
![]() |
ba5ad234ee | ||
![]() |
eb723e9492 | ||
![]() |
45106d2b70 | ||
![]() |
0e87ddfa4b | ||
![]() |
9e3532c81b | ||
![]() |
9a162e9502 |
2
Makefile
2
Makefile
@@ -1,6 +1,6 @@
|
||||
|
||||
USER_GH=eyedeekay
|
||||
VERSION=0.33.001
|
||||
VERSION=0.33.2
|
||||
packagename=sam3
|
||||
|
||||
echo:
|
||||
|
27
README.md
27
README.md
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
|
@@ -1,5 +1,3 @@
|
||||
// +build nettest
|
||||
|
||||
package sam3
|
||||
|
||||
import (
|
||||
|
11
emit.go
11
emit.go
@@ -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
1
go.mod
@@ -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
2
go.sum
@@ -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=
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
}
|
@@ -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())
|
||||
}
|
18
primary.go
18
primary.go
@@ -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 {
|
||||
|
@@ -1,5 +1,3 @@
|
||||
// +build nettest
|
||||
|
||||
package sam3
|
||||
|
||||
import (
|
||||
|
@@ -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
2
raw.go
@@ -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
|
||||
|
@@ -6,7 +6,7 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/eyedeekay/sam3/i2pkeys"
|
||||
"github.com/eyedeekay/i2pkeys"
|
||||
)
|
||||
|
||||
type SAMResolver struct {
|
||||
|
4
sam3.go
4
sam3.go
@@ -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.
|
||||
|
@@ -1,5 +1,3 @@
|
||||
// +build nettest
|
||||
|
||||
package sam3
|
||||
|
||||
import (
|
||||
|
109
stream.go
109
stream.go
@@ -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
116
streamListener.go
Normal 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
|
||||
}
|
@@ -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) {
|
||||
|
@@ -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()
|
||||
|
Reference in New Issue
Block a user