Files
goSam/client.go

287 lines
6.5 KiB
Go
Raw Normal View History

package goSam
import (
"bufio"
2019-04-25 23:45:24 -04:00
"crypto/sha256"
"encoding/base32"
"encoding/base64"
2019-04-25 23:45:24 -04:00
"encoding/binary"
"fmt"
"math"
"math/rand"
"net"
2019-04-25 23:45:24 -04:00
"strings"
2021-04-15 19:16:11 -04:00
"sync"
"time"
2022-03-10 00:54:43 -05:00
"github.com/eyedeekay/i2pkeys"
//samkeys "github.com/eyedeekay/goSam/compat"
)
// A Client represents a single Connection to the SAM bridge
type Client struct {
2019-04-25 23:45:24 -04:00
host string
port string
fromport string
toport string
2022-04-28 11:23:54 -04:00
user string
pass string
SamConn net.Conn // Control socket
SamDGConn net.PacketConn // Datagram socket
rd *bufio.Reader
// d *Client
sigType string
destination string
2019-02-24 23:08:01 -05:00
inLength uint
inVariance int
inQuantity uint
inBackups uint
outLength uint
outVariance int
outQuantity uint
outBackups uint
dontPublishLease bool
encryptLease bool
2020-09-03 16:19:12 -04:00
leaseSetEncType string
reduceIdle bool
reduceIdleTime uint
reduceIdleQuantity uint
closeIdle bool
closeIdleTime uint
2019-02-24 23:08:01 -05:00
compression bool
2019-02-21 17:38:11 -05:00
debug bool
2021-04-15 19:16:11 -04:00
mutex sync.Mutex
2019-02-27 20:46:18 -05:00
//NEVER, EVER modify lastaddr or id yourself. They are used internally only.
id int32
sammin int
sammax int
}
// SAMsigTypes is a slice of the available signature types
2019-02-24 23:08:01 -05:00
var SAMsigTypes = []string{
"SIGNATURE_TYPE=DSA_SHA1",
"SIGNATURE_TYPE=ECDSA_SHA256_P256",
"SIGNATURE_TYPE=ECDSA_SHA384_P384",
"SIGNATURE_TYPE=ECDSA_SHA512_P521",
"SIGNATURE_TYPE=EdDSA_SHA512_Ed25519",
}
var ValidSAMCommands = []string{
"HELLO",
"SESSION",
"STREAM",
}
var (
i2pB64enc *base64.Encoding = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~")
i2pB32enc *base32.Encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
)
2014-02-14 12:05:13 +01:00
// NewDefaultClient creates a new client, connecting to the default host:port at localhost:7656
func NewDefaultClient() (*Client, error) {
return NewClient("localhost:7656")
}
2014-02-14 12:05:13 +01:00
// NewClient creates a new client, connecting to a specified port
func NewClient(addr string) (*Client, error) {
return NewClientFromOptions(SetAddr(addr))
}
func NewID() int32 {
id := rand.Int31n(math.MaxInt32)
fmt.Printf("Initializing new ID: %d\n", id)
return id
}
2019-02-27 20:46:18 -05:00
// NewID generates a random number to use as an tunnel name
func (c *Client) NewID() int32 {
if c.id == 0 {
c.id = NewID()
}
return c.id
}
// Destination returns the full destination of the local tunnel
func (c *Client) Destination() string {
return c.destination
}
// Base32 returns the base32 of the local tunnel
func (c *Client) Base32() string {
// hash := sha256.New()
2019-05-16 18:56:59 -04:00
b64, err := i2pB64enc.DecodeString(c.Base64())
2019-05-16 18:56:33 -04:00
if err != nil {
return ""
}
//hash.Write([]byte(b64))
var s []byte
for _, e := range sha256.Sum256(b64) {
s = append(s, e)
}
return strings.ToLower(strings.Replace(i2pB32enc.EncodeToString(s), "=", "", -1))
}
func (c *Client) base64() []byte {
if c.destination != "" {
s, _ := i2pB64enc.DecodeString(c.destination)
alen := binary.BigEndian.Uint16(s[385:387])
return s[:387+alen]
}
return []byte("")
}
// Base64 returns the base64 of the local tunnel
func (c *Client) Base64() string {
return i2pB64enc.EncodeToString(c.base64())
}
// NewClientFromOptions creates a new client, connecting to a specified port
func NewClientFromOptions(opts ...func(*Client) error) (*Client, error) {
var c Client
c.host = "127.0.0.1"
c.port = "7656"
c.inLength = 3
c.inVariance = 0
c.inQuantity = 3
c.inBackups = 1
c.outLength = 3
c.outVariance = 0
c.outQuantity = 3
c.outBackups = 1
c.dontPublishLease = true
c.encryptLease = false
c.reduceIdle = false
c.reduceIdleTime = 300000
c.reduceIdleQuantity = 1
c.closeIdle = true
c.closeIdleTime = 600000
c.debug = false
c.sigType = SAMsigTypes[4]
c.id = 0
2019-03-14 22:46:17 -04:00
c.destination = ""
2020-09-03 16:19:12 -04:00
c.leaseSetEncType = "4,0"
c.fromport = ""
c.toport = ""
c.sammin = 0
c.sammax = 1
for _, o := range opts {
if err := o(&c); err != nil {
return nil, err
}
}
c.id = c.NewID()
conn, err := net.DialTimeout("tcp", c.samaddr(), 15*time.Minute)
if err != nil {
return nil, err
}
if c.debug {
2019-12-08 16:57:51 -05:00
conn = WrapConn(conn)
2015-03-25 21:37:41 +01:00
}
c.SamConn = conn
c.rd = bufio.NewReader(conn)
return &c, c.hello()
}
// ID returns a the current ID of the client as a string
2019-04-25 23:45:24 -04:00
func (p *Client) ID() string {
return fmt.Sprintf("%d", p.NewID())
2019-04-25 23:45:24 -04:00
}
// Addr returns the address of the client as a net.Addr
2019-07-05 03:10:01 -04:00
func (p *Client) Addr() net.Addr {
2022-03-10 00:54:43 -05:00
keys := i2pkeys.I2PAddr(p.Destination())
return keys
}
func (p *Client) LocalAddr() net.Addr {
return p.Addr()
2019-07-05 03:10:01 -04:00
}
// LocalKeys returns the local keys of the client as a fully-fledged i2pkeys.I2PKeys
func (p *Client) PrivateAddr() i2pkeys.I2PKeys {
//keys := i2pkeys.I2PAddr(p.Destination())
keys := i2pkeys.NewKeys(i2pkeys.I2PAddr(p.base64()), p.Destination())
return keys
}
2022-11-18 00:04:49 -05:00
// return the combined host:port of the SAM bridge
func (c *Client) samaddr() string {
return fmt.Sprintf("%s:%s", c.host, c.port)
}
2014-02-11 13:10:24 +01:00
// send the initial handshake command and check that the reply is ok
2015-03-25 22:03:05 +01:00
func (c *Client) hello() error {
2022-04-28 11:23:54 -04:00
r, err := c.sendCmd("HELLO VERSION MIN=3.%d MAX=3.%d %s %s\n", c.sammin, c.sammax, c.getUser(), c.getPass())
2014-02-11 13:10:24 +01:00
if err != nil {
return err
}
2014-02-11 13:10:24 +01:00
if r.Topic != "HELLO" {
return fmt.Errorf("Client Hello Unknown Reply: %+v\n", r)
2014-02-11 13:10:24 +01:00
}
if r.Pairs["RESULT"] != "OK" {
2014-02-11 13:10:24 +01:00
return fmt.Errorf("Handshake did not succeed\nReply:%+v\n", r)
}
2014-02-11 13:10:24 +01:00
return nil
}
// helper to send one command and parse the reply by sam
2015-03-25 22:03:05 +01:00
func (c *Client) sendCmd(str string, args ...interface{}) (*Reply, error) {
if _, err := fmt.Fprintf(c.SamConn, str, args...); err != nil {
return nil, err
}
2015-03-25 21:37:41 +01:00
line, err := c.rd.ReadString('\n')
if err != nil {
2015-03-25 22:03:05 +01:00
return nil, err
}
2014-02-11 13:10:24 +01:00
2015-03-25 21:37:41 +01:00
return parseReply(line)
}
// Close the underlying socket to SAM
func (c *Client) Close() error {
2015-03-25 22:03:05 +01:00
c.rd = nil
return c.SamConn.Close()
}
// NewClient generates an exact copy of the client with the same options, but
// re-does all the handshaky business so that Dial can pick up right where it
// left off, should the need arise.
func (c *Client) NewClient(id int32) (*Client, error) {
return NewClientFromOptions(
SetHost(c.host),
SetPort(c.port),
SetDebug(c.debug),
SetInLength(c.inLength),
SetOutLength(c.outLength),
SetInVariance(c.inVariance),
SetOutVariance(c.outVariance),
SetInQuantity(c.inQuantity),
SetOutQuantity(c.outQuantity),
SetInBackups(c.inBackups),
SetOutBackups(c.outBackups),
SetUnpublished(c.dontPublishLease),
SetEncrypt(c.encryptLease),
SetReduceIdle(c.reduceIdle),
SetReduceIdleTime(c.reduceIdleTime),
SetReduceIdleQuantity(c.reduceIdleQuantity),
SetCloseIdle(c.closeIdle),
SetCloseIdleTime(c.closeIdleTime),
SetCompression(c.compression),
setid(id),
)
}